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};
6366std::atomic<float > steering_{0 .0f };
6467// Track last joystick message time for timeout detection
6568std::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
6770std::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
7079void 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
7787void 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
8293void 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
88100void 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
108121void 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 (" \n Received 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;
0 commit comments