File tree Expand file tree Collapse file tree
executors/events_cbg_executor Expand file tree Collapse file tree Original file line number Diff line number Diff line change 2323
2424#include " rcl/wait.h"
2525
26+ #include " rclcpp/logging.hpp"
2627#include " rclcpp/macros.hpp"
2728#include " rclcpp/wait_result_kind.hpp"
2829
@@ -137,7 +138,16 @@ class WaitResult final
137138 ~WaitResult ()
138139 {
139140 if (wait_set_pointer_) {
140- wait_set_pointer_->wait_result_release ();
141+ // wait_result_release() throws if the wait set is no longer holding this result.
142+ // A destructor is noexcept, so letting that escape would terminate the process.
143+ try {
144+ wait_set_pointer_->wait_result_release ();
145+ } catch (const std::exception & exc) {
146+ RCLCPP_ERROR (
147+ rclcpp::get_logger (" rclcpp" ), " unhandled exception in ~WaitResult(): %s" , exc.what ());
148+ } catch (...) {
149+ RCLCPP_ERROR (rclcpp::get_logger (" rclcpp" ), " unhandled exception in ~WaitResult()" );
150+ }
141151 }
142152 }
143153
Original file line number Diff line number Diff line change @@ -116,7 +116,18 @@ EventsCBGExecutor::EventsCBGExecutor(
116116
117117EventsCBGExecutor::~EventsCBGExecutor ()
118118{
119- shutdown ();
119+ // shutdown() removes nodes and callback groups, which throws when an entity is no longer
120+ // associated with this executor. A destructor is noexcept, so letting that escape would
121+ // terminate the process.
122+ try {
123+ shutdown ();
124+ } catch (const std::exception & exc) {
125+ RCLCPP_ERROR (
126+ rclcpp::get_logger (" rclcpp" ),
127+ " unhandled exception in ~EventsCBGExecutor(): %s" , exc.what ());
128+ } catch (...) {
129+ RCLCPP_ERROR (rclcpp::get_logger (" rclcpp" ), " unhandled exception in ~EventsCBGExecutor()" );
130+ }
120131}
121132
122133void EventsCBGExecutor::shutdown ()
Original file line number Diff line number Diff line change @@ -203,7 +203,16 @@ class TimeSource::NodeState final
203203 node_base_ || node_topics_ || node_graph_ || node_services_ ||
204204 node_logging_ || node_clock_ || node_parameters_)
205205 {
206- detachNode ();
206+ // detachNode() removes the parameter callbacks, which throws while a set-parameters
207+ // callback is running. A destructor is noexcept, so letting that escape would terminate
208+ // the process.
209+ try {
210+ detachNode ();
211+ } catch (const std::exception & exc) {
212+ RCLCPP_ERROR (logger_, " unhandled exception in ~NodeState(): %s" , exc.what ());
213+ } catch (...) {
214+ RCLCPP_ERROR (logger_, " unhandled exception in ~NodeState()" );
215+ }
207216 }
208217 }
209218
Original file line number Diff line number Diff line change 2424#include < stdexcept>
2525#include < string>
2626#include < thread>
27+ #include < vector>
2728
2829#include " rcl/error_handling.h"
2930#include " rcl/time.h"
@@ -203,6 +204,27 @@ TEST_F(TestTimeSource, detachUnattached) {
203204 ASSERT_NO_THROW (ts.detachNode ());
204205}
205206
207+ TEST_F (TestTimeSource, destructor_does_not_terminate_when_detach_throws) {
208+ auto time_source = std::make_unique<rclcpp::TimeSource>(node);
209+
210+ // Destroying the time source from inside a set-parameters callback makes its destructor call
211+ // NodeParameters::remove_on_set_parameters_callback(), which throws because parameter
212+ // modification is disabled while such a callback runs. A destructor is implicitly noexcept, so
213+ // an unguarded throw here terminates the process instead of failing this expectation.
214+ auto handle = node->add_on_set_parameters_callback (
215+ [&time_source](const std::vector<rclcpp::Parameter> &) {
216+ time_source.reset ();
217+ rcl_interfaces::msg::SetParametersResult result;
218+ result.successful = true ;
219+ return result;
220+ });
221+
222+ EXPECT_NO_THROW (node->set_parameter ({" use_sim_time" , true }));
223+ EXPECT_EQ (nullptr , time_source);
224+
225+ node->remove_on_set_parameters_callback (handle.get ());
226+ }
227+
206228TEST_F (TestTimeSource, reattach) {
207229 rclcpp::TimeSource ts;
208230 // Try reattach
You can’t perform that action at this time.
0 commit comments