diff --git a/rclcpp/include/rclcpp/graph_listener.hpp b/rclcpp/include/rclcpp/graph_listener.hpp index 901ba46..2adbad4 100644 --- a/rclcpp/include/rclcpp/graph_listener.hpp +++ b/rclcpp/include/rclcpp/graph_listener.hpp @@ -177,7 +177,6 @@ private: std::vector node_graph_interfaces_; rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition(); - std::shared_ptr interrupt_guard_condition_context_; rcl_guard_condition_t * shutdown_guard_condition_; rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set(); }; diff --git a/rclcpp/src/rclcpp/graph_listener.cpp b/rclcpp/src/rclcpp/graph_listener.cpp index 2936932..5c5407a 100644 --- a/rclcpp/src/rclcpp/graph_listener.cpp +++ b/rclcpp/src/rclcpp/graph_listener.cpp @@ -39,17 +39,15 @@ GraphListener::GraphListener(std::shared_ptr parent_context) : parent_context_(parent_context), is_started_(false), is_shutdown_(false), - interrupt_guard_condition_context_(nullptr), shutdown_guard_condition_(nullptr) { // TODO(wjwwood): make a guard condition class in rclcpp so this can be tracked // automatically with the rcl guard condition // hold on to this context to prevent it from going out of scope while this // guard condition is using it. - interrupt_guard_condition_context_ = parent_context->get_rcl_context(); rcl_ret_t ret = rcl_guard_condition_init( &interrupt_guard_condition_, - interrupt_guard_condition_context_.get(), + parent_context->get_rcl_context().get(), rcl_guard_condition_get_default_options()); if (RCL_RET_OK != ret) { throw_from_rcl_error(ret, "failed to create interrupt guard condition"); @@ -72,6 +70,10 @@ GraphListener::start_if_not_started() } if (!is_started_) { // Initialize the wait set before starting. + auto parent_context = parent_context_.lock(); + if (!parent_context) { + throw std::runtime_error("parent context was destroyed"); + } rcl_ret_t ret = rcl_wait_set_init( &wait_set_, 0, // number_of_subscriptions @@ -80,7 +82,7 @@ GraphListener::start_if_not_started() 0, // number_of_clients 0, // number_of_services 0, // number_of_events - interrupt_guard_condition_context_.get(), + parent_context->get_rcl_context().get(), rcl_get_default_allocator()); if (RCL_RET_OK != ret) { throw_from_rcl_error(ret, "failed to initialize wait set"); @@ -141,6 +143,13 @@ GraphListener::run_loop() } // This lock is released when the loop continues or exits. std::lock_guard nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock); + // Ensure that the context doesn't go out of scope. + auto parent_context = parent_context_.lock(); + if (!parent_context) { + // The parent context may be destroyed before this loop is stopped. + // In that case, the loop is broken and the function just returns silently. + return; + } // Resize the wait set if necessary. const size_t node_graph_interfaces_size = node_graph_interfaces_.size(); @@ -350,7 +359,6 @@ GraphListener::__shutdown(bool should_throw) listener_thread_.join(); } rcl_ret_t ret = rcl_guard_condition_fini(&interrupt_guard_condition_); - interrupt_guard_condition_context_.reset(); // release context guard condition was using if (RCL_RET_OK != ret) { throw_from_rcl_error(ret, "failed to finalize interrupt guard condition"); }