15 #include "rclcpp/graph_listener.hpp"
23 #include "rcl/error_handling.h"
25 #include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
26 #include "rclcpp/exceptions.hpp"
27 #include "rclcpp/logging.hpp"
28 #include "rclcpp/node.hpp"
29 #include "rmw/impl/cpp/demangle.hpp"
31 #include "rcutils/logging_macros.h"
33 using rclcpp::exceptions::throw_from_rcl_error;
37 namespace graph_listener
40 GraphListener::GraphListener(
const std::shared_ptr<Context> & parent_context)
41 : weak_parent_context_(parent_context),
42 rcl_parent_context_(parent_context->get_rcl_context()),
45 interrupt_guard_condition_(parent_context)
49 GraphListener::~GraphListener()
51 GraphListener::shutdown(std::nothrow);
54 void GraphListener::init_wait_set()
64 rcl_parent_context_.get(),
67 throw_from_rcl_error(ret,
"failed to initialize wait set");
72 GraphListener::start_if_not_started()
74 auto parent_context = weak_parent_context_.lock();
76 std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
77 if (is_shutdown_.load()) {
80 if (is_started_ || !parent_context) {
89 std::weak_ptr<GraphListener> weak_this = shared_from_this();
90 auto callback_handle = parent_context->add_on_shutdown_callback(
92 auto shared_this = weak_this.lock();
95 shared_this->shutdown(std::nothrow);
99 bool started_on_this_thread =
false;
101 std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
102 if (!is_shutdown_.load() && !is_started_) {
106 listener_thread_ = std::thread(&GraphListener::run,
this);
108 started_on_this_thread =
true;
111 if (!started_on_this_thread) {
114 parent_context->remove_on_shutdown_callback(callback_handle);
115 if (is_shutdown_.load()) {
126 }
catch (
const std::exception & exc) {
127 RCUTILS_LOG_ERROR_NAMED(
129 "caught %s exception in GraphListener thread: %s",
130 rmw::impl::cpp::demangle(exc).c_str(),
132 std::rethrow_exception(std::current_exception());
134 RCUTILS_LOG_ERROR_NAMED(
136 "unknown error in GraphListener thread");
137 std::rethrow_exception(std::current_exception());
142 GraphListener::run_loop()
146 if (is_shutdown_.load()) {
153 std::lock_guard<std::mutex> nodes_barrier_lock(node_graph_interfaces_barrier_mutex_);
155 node_graph_interfaces_mutex_.lock();
158 std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
160 const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
162 if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_size + 2)) {
165 throw_from_rcl_error(ret,
"failed to resize wait set");
171 throw_from_rcl_error(ret,
"failed to clear wait set");
174 detail::add_guard_condition_to_rcl_wait_set(wait_set_, interrupt_guard_condition_);
177 std::vector<size_t> graph_gc_indexes(node_graph_interfaces_size, 0u);
178 for (
size_t i = 0u; i < node_graph_interfaces_size; ++i) {
179 auto node_ptr = node_graph_interfaces_[i];
181 if (node_ptr->count_graph_users() == 0) {
185 auto graph_gc = node_ptr->get_graph_guard_condition();
187 throw_from_rcl_error(
RCL_RET_ERROR,
"failed to get graph guard condition");
191 throw_from_rcl_error(ret,
"failed to add graph guard condition to wait set");
198 throw std::runtime_error(
"rcl_wait unexpectedly timed out");
201 throw_from_rcl_error(ret,
"failed to wait on wait set");
205 for (
size_t i = 0u; i < node_graph_interfaces_size; ++i) {
206 const auto node_ptr = node_graph_interfaces_[i];
207 auto graph_gc = node_ptr->get_graph_guard_condition();
209 throw_from_rcl_error(
RCL_RET_ERROR,
"failed to get graph guard condition");
211 if (graph_gc == wait_set_.guard_conditions[graph_gc_indexes[i]]) {
212 node_ptr->notify_graph_change();
216 node_ptr->notify_shutdown();
223 interrupt_(GuardCondition * interrupt_guard_condition)
225 interrupt_guard_condition->trigger();
230 std::mutex * node_graph_interfaces_barrier_mutex,
231 std::mutex * node_graph_interfaces_mutex,
232 GuardCondition * interrupt_guard_condition)
237 std::lock_guard<std::mutex> nodes_barrier_lock(*node_graph_interfaces_barrier_mutex);
239 interrupt_(interrupt_guard_condition);
240 node_graph_interfaces_mutex->lock();
246 std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
249 for (
const auto node_ptr : (*node_graph_interfaces)) {
250 if (node_graph == node_ptr) {
266 &node_graph_interfaces_barrier_mutex_,
267 &node_graph_interfaces_mutex_,
268 &interrupt_guard_condition_);
270 std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
271 return has_node_(&node_graph_interfaces_, node_graph);
278 throw std::invalid_argument(
"node is nullptr");
280 std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
281 if (is_shutdown_.load()) {
288 &node_graph_interfaces_barrier_mutex_,
289 &node_graph_interfaces_mutex_,
290 &interrupt_guard_condition_);
292 std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
293 if (has_node_(&node_graph_interfaces_, node_graph)) {
296 node_graph_interfaces_.push_back(node_graph);
303 std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
307 for (
auto it = node_graph_interfaces->begin(); it != node_graph_interfaces->end(); ++it) {
308 if (node_graph == *it) {
310 node_graph_interfaces->erase(it);
316 throw NodeNotFoundError();
323 throw std::invalid_argument(
"node is nullptr");
325 std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
328 return remove_node_(&node_graph_interfaces_, node_graph);
334 &node_graph_interfaces_barrier_mutex_,
335 &node_graph_interfaces_mutex_,
336 &interrupt_guard_condition_);
338 std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
339 remove_node_(&node_graph_interfaces_, node_graph);
343 GraphListener::cleanup_wait_set()
347 throw_from_rcl_error(ret,
"failed to finalize wait set");
352 GraphListener::__shutdown()
354 std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
355 if (!is_shutdown_.exchange(
true)) {
357 interrupt_(&interrupt_guard_condition_);
358 listener_thread_.join();
367 GraphListener::shutdown()
373 GraphListener::shutdown(
const std::nothrow_t &) noexcept
377 }
catch (
const std::exception & exc) {
380 "caught %s exception when shutting down GraphListener: %s",
381 rmw::impl::cpp::demangle(exc).c_str(), exc.what());
385 "caught unknown exception when shutting down GraphListener");
390 GraphListener::is_shutdown()
392 return is_shutdown_.load();
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Thrown when a function is called on a GraphListener that is already shutdown.
Thrown when a node has already been added to the GraphListener.
Pure virtual interface class for the NodeGraph part of the Node API.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
#define RCL_RET_OK
Success return code.
#define RCL_RET_ERROR
Unspecified error return code.
#define RCL_RET_TIMEOUT
Timeout occurred return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_init(rcl_wait_set_t *wait_set, size_t number_of_subscriptions, size_t number_of_guard_conditions, size_t number_of_timers, size_t number_of_clients, size_t number_of_services, size_t number_of_events, rcl_context_t *context, rcl_allocator_t allocator)
Initialize a rcl wait set with space for items to be waited on.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_clear(rcl_wait_set_t *wait_set)
Remove (sets to NULL) all entities in the wait set.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_fini(rcl_wait_set_t *wait_set)
Finalize a rcl wait set.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait(rcl_wait_set_t *wait_set, int64_t timeout)
Block until the wait set is ready or until the timeout has been exceeded.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_resize(rcl_wait_set_t *wait_set, size_t subscriptions_size, size_t guard_conditions_size, size_t timers_size, size_t clients_size, size_t services_size, size_t events_size)
Reallocate space for entities in the wait set.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_guard_condition(rcl_wait_set_t *wait_set, const rcl_guard_condition_t *guard_condition, size_t *index)
Store a pointer to the guard condition in the next empty spot in the set.