Skip to content

Commit a0cca93

Browse files
committed
gui cleanup
1 parent 05577a0 commit a0cca93

2 files changed

Lines changed: 117 additions & 29 deletions

File tree

src/gui/gui_main.cc

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include <signal.h>
2727
#include <thread>
2828
#include <chrono>
29+
#include <mutex>
30+
#include <errno.h>
31+
#include <time.h>
2932
#include <QApplication>
3033
#include <QPushButton>
3134
#include <QMetaType>
@@ -63,29 +66,39 @@ std::atomic<float> throttle_{0.0f};
6366
std::atomic<float> steering_{0.0f};
6467
// Track last joystick message time for timeout detection
6568
std::atomic<std::chrono::steady_clock::time_point> last_joystick_time_{std::chrono::steady_clock::time_point{}};
66-
// ROS node shared pointer for proper cleanup
69+
// ROS node and subscriptions for proper cleanup
6770
std::shared_ptr<rclcpp::Node> ros_node_ = nullptr;
71+
rclcpp::Subscription<CarStatusMsg>::SharedPtr status_sub_ = nullptr;
72+
rclcpp::Subscription<LaserScan>::SharedPtr lidar_sub_ = nullptr;
73+
rclcpp::Subscription<AckermannCurvatureDriveMsg>::SharedPtr drive_sub_ = nullptr;
74+
rclcpp::Subscription<Image>::SharedPtr camera_sub_ = nullptr;
75+
rclcpp::Subscription<Joy>::SharedPtr joystick_sub_ = nullptr;
76+
std::mutex cleanup_mutex_;
6877
} // namespace
6978

7079
void StatusCallback(const CarStatusMsg::SharedPtr msg) {
80+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
7181
if (!run_.load() || main_window_ == nullptr || !rclcpp::ok()) return;
7282
drive_mode_.store(msg->status);
7383
battery_voltage_.store(msg->battery_voltage);
7484
vesc_okay_.store(true);
7585
}
7686

7787
void LidarCallback(const LaserScan::SharedPtr /*msg*/) {
88+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
7889
if (!run_.load() || !rclcpp::ok()) return;
7990
lidar_okay_.store(true);
8091
}
8192

8293
void DriveCallback(const AckermannCurvatureDriveMsg::SharedPtr msg) {
94+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
8395
if (!run_.load() || !rclcpp::ok()) return;
8496
throttle_.store(msg->velocity);
8597
steering_.store(msg->curvature);
8698
}
8799

88100
void CameraCallback(const Image::SharedPtr msg) {
101+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
89102
if (!run_.load() || main_window_ == nullptr || !rclcpp::ok()) return;
90103

91104
try {
@@ -106,6 +119,7 @@ void CameraCallback(const Image::SharedPtr msg) {
106119

107120
// Joystick callback - marks joystick as okay when messages are received
108121
void JoystickCallback(const Joy::SharedPtr msg) {
122+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
109123
if (!run_.load() || !rclcpp::ok()) return;
110124

111125
// Update the last joystick message timestamp
@@ -118,15 +132,17 @@ void* RosThread(void* arg) {
118132
// pthread_detach(pthread_self());
119133

120134
ros_node_ = rclcpp::Node::make_shared("ut_automata_gui");
121-
auto status_sub = ros_node_->create_subscription<CarStatusMsg>(
135+
136+
// Store subscriptions as shared pointers for proper cleanup
137+
status_sub_ = ros_node_->create_subscription<CarStatusMsg>(
122138
"car_status", 10u, &StatusCallback);
123-
auto lidar_sub = ros_node_->create_subscription<LaserScan>(
139+
lidar_sub_ = ros_node_->create_subscription<LaserScan>(
124140
"scan", 10u, &LidarCallback);
125-
auto drive_sub = ros_node_->create_subscription<AckermannCurvatureDriveMsg>(
141+
drive_sub_ = ros_node_->create_subscription<AckermannCurvatureDriveMsg>(
126142
"ackermann_curvature_drive", 10u, &DriveCallback);
127-
auto camera_sub = ros_node_->create_subscription<Image>(
143+
camera_sub_ = ros_node_->create_subscription<Image>(
128144
"/camera_0/image_raw", 10u, &CameraCallback);
129-
auto joystick_sub = ros_node_->create_subscription<Joy>(
145+
joystick_sub_ = ros_node_->create_subscription<Joy>(
130146
"joystick", 10u, &JoystickCallback);
131147

132148
RateLoop loop(5.0);
@@ -174,12 +190,63 @@ void* RosThread(void* arg) {
174190

175191
// Clean up subscriptions and node before exiting thread
176192
printf("ROS thread shutting down, cleaning up resources...\n");
177-
status_sub.reset();
178-
lidar_sub.reset();
179-
drive_sub.reset();
180-
camera_sub.reset();
181-
joystick_sub.reset();
182-
ros_node_.reset();
193+
194+
// Lock to prevent callbacks from running during cleanup
195+
{
196+
std::lock_guard<std::mutex> lock(cleanup_mutex_);
197+
198+
// First check if rclcpp is still okay before attempting cleanup
199+
if (rclcpp::ok() && ros_node_) {
200+
// Reset subscriptions one by one with error handling
201+
try {
202+
if (status_sub_) {
203+
status_sub_.reset();
204+
printf("Status subscription cleaned up\n");
205+
}
206+
if (lidar_sub_) {
207+
lidar_sub_.reset();
208+
printf("Lidar subscription cleaned up\n");
209+
}
210+
if (drive_sub_) {
211+
drive_sub_.reset();
212+
printf("Drive subscription cleaned up\n");
213+
}
214+
if (camera_sub_) {
215+
camera_sub_.reset();
216+
printf("Camera subscription cleaned up\n");
217+
}
218+
if (joystick_sub_) {
219+
joystick_sub_.reset();
220+
printf("Joystick subscription cleaned up\n");
221+
}
222+
} catch (const std::exception& e) {
223+
printf("Exception during subscription cleanup: %s\n", e.what());
224+
}
225+
226+
// Small delay to ensure all cleanup operations complete
227+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
228+
229+
// Reset the node after all subscriptions are cleaned up
230+
try {
231+
if (ros_node_) {
232+
ros_node_.reset();
233+
printf("Node cleaned up\n");
234+
}
235+
} catch (const std::exception& e) {
236+
printf("Exception during node cleanup: %s\n", e.what());
237+
}
238+
} else {
239+
printf("ROS2 context already shut down, skipping subscription cleanup\n");
240+
// Just reset the pointers without attempting ROS cleanup
241+
status_sub_.reset();
242+
lidar_sub_.reset();
243+
drive_sub_.reset();
244+
camera_sub_.reset();
245+
joystick_sub_.reset();
246+
ros_node_.reset();
247+
}
248+
}
249+
183250
printf("ROS thread cleanup complete.\n");
184251

185252
pthread_exit(NULL);
@@ -190,14 +257,10 @@ void SignalHandler(int num) {
190257
printf("\nReceived signal %d, shutting down gracefully...\n", num);
191258
run_.store(false);
192259

193-
// Give the ROS thread a moment to clean up
194-
std::this_thread::sleep_for(std::chrono::milliseconds(100));
195-
196-
// Shutdown rclcpp if initialized
197-
if (rclcpp::ok()) {
198-
rclcpp::shutdown();
199-
}
260+
// Give the ROS thread time to process the shutdown signal
261+
std::this_thread::sleep_for(std::chrono::milliseconds(200));
200262

263+
// Force exit for signals that require immediate shutdown
201264
if (num == SIGINT) {
202265
exit(0);
203266
} else {
@@ -220,22 +283,47 @@ int main(int argc, char *argv[]) {
220283

221284
const int retval = app.exec();
222285

223-
// Signal shutdown and wait for ROS thread to finish
224-
printf("Application exiting, signaling ROS thread to stop...\n");
286+
// Application is closing - initiate shutdown sequence
287+
printf("Application exiting, initiating clean shutdown...\n");
225288
run_.store(false);
226-
pthread_join(ptid, NULL);
227-
printf("ROS thread joined successfully.\n");
228289

229-
// Clean up GUI before shutting down ROS
290+
// Wait for ROS thread to finish with a timeout
291+
printf("Waiting for ROS thread to complete...\n");
292+
struct timespec timeout;
293+
clock_gettime(CLOCK_REALTIME, &timeout);
294+
timeout.tv_sec += 5; // Increased timeout to 5 seconds
295+
296+
int join_result = pthread_timedjoin_np(ptid, NULL, &timeout);
297+
if (join_result == ETIMEDOUT) {
298+
printf("ROS thread join timed out, forcing shutdown...\n");
299+
pthread_cancel(ptid);
300+
pthread_join(ptid, NULL); // Wait for cancellation to complete
301+
} else if (join_result == 0) {
302+
printf("ROS thread joined successfully.\n");
303+
} else {
304+
printf("ROS thread join failed with error: %d\n", join_result);
305+
}
306+
307+
// Clean up GUI first
308+
printf("Cleaning up GUI...\n");
230309
delete main_window_;
231310
main_window_ = nullptr;
232311
printf("Main window deleted.\n");
233312

234-
// Shutdown ROS2 cleanly
313+
// Add a small delay to ensure all cleanup operations are complete
314+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
315+
316+
// Final ROS2 shutdown - only if still initialized
235317
if (rclcpp::ok()) {
236318
printf("Shutting down rclcpp...\n");
237-
rclcpp::shutdown();
238-
printf("rclcpp shutdown complete.\n");
319+
try {
320+
rclcpp::shutdown();
321+
printf("rclcpp shutdown complete.\n");
322+
} catch (const std::exception& e) {
323+
printf("Exception during rclcpp shutdown: %s\n", e.what());
324+
}
325+
} else {
326+
printf("rclcpp already shut down.\n");
239327
}
240328

241329
return retval;

src/gui/gui_mainwindow.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,8 @@ MainWindow::MainWindow(QWidget* parent) :
327327
}
328328

329329
MainWindow::~MainWindow() {
330-
// Clean shutdown - the main thread should have already signaled shutdown
331-
// but we ensure the GUI elements are properly cleaned up
330+
// Clean shutdown - ensure Qt widgets are properly cleaned up
331+
// The main thread should handle ROS cleanup before destroying the GUI
332332
}
333333

334334
std::vector<std::string> Split(const std::string& s) {

0 commit comments

Comments
 (0)