15 #include "rclcpp/context.hpp"
22 #include <unordered_set>
28 #include "rclcpp/detail/utilities.hpp"
29 #include "rclcpp/exceptions.hpp"
30 #include "rclcpp/logging.hpp"
31 #include "rclcpp/graph_listener.hpp"
32 #include "rcpputils/scope_exit.hpp"
33 #include "rcutils/error_handling.h"
34 #include "rcutils/macros.h"
36 #include "./logging_mutex.hpp"
49 add_context(
const Context::SharedPtr & context)
51 std::lock_guard<std::mutex> guard(mutex_);
52 weak_contexts_.push_back(context);
56 remove_context(
const Context * context)
58 std::lock_guard<std::mutex> guard(mutex_);
61 weak_contexts_.begin(),
63 [context](
const Context::WeakPtr weak_context) {
64 auto locked_context = weak_context.lock();
65 if (!locked_context) {
69 return locked_context.get() == context;
72 weak_contexts_.end());
75 std::vector<Context::SharedPtr>
78 std::lock_guard<std::mutex> lock(mutex_);
79 std::vector<Context::SharedPtr> shared_contexts;
80 for (
auto it = weak_contexts_.begin(); it != weak_contexts_.end(); ) {
81 auto context_ptr = it->lock();
84 it = weak_contexts_.erase(it);
87 shared_contexts.push_back(context_ptr);
90 return shared_contexts;
94 std::vector<std::weak_ptr<rclcpp::Context>> weak_contexts_;
103 WeakContextsWrapper::SharedPtr
106 static WeakContextsWrapper::SharedPtr weak_contexts = WeakContextsWrapper::make_shared();
107 if (!weak_contexts) {
108 throw std::runtime_error(
"weak contexts vector is not valid");
110 return weak_contexts;
116 get_logging_reference_count()
118 static size_t ref_count = 0;
126 rclcpp_logging_output_handler(
127 const rcutils_log_location_t * location,
128 int severity,
const char * name, rcutils_time_point_value_t timestamp,
129 const char * format, va_list * args)
132 std::shared_ptr<std::recursive_mutex> logging_mutex;
133 logging_mutex = get_global_logging_mutex();
134 std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
136 location, severity, name, timestamp, format, args);
137 }
catch (std::exception & ex) {
138 RCUTILS_SAFE_FWRITE_TO_STDERR(ex.what());
139 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
141 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to take global rclcpp logging mutex\n");
147 : rcl_context_(nullptr),
148 shutdown_reason_(
""),
149 logging_mutex_(nullptr)
156 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
158 this->
shutdown(
"context destructor was called while still not shutdown");
162 }
catch (
const std::exception & exc) {
163 RCLCPP_ERROR(
rclcpp::get_logger(
"rclcpp"),
"unhandled exception in ~Context(): %s", exc.what());
176 rclcpp::get_logger(
"rclcpp"),
"rcl context unexpectedly not shutdown during cleanup");
183 "failed to finalize context: %s", rcl_get_error_string().str);
194 char const *
const * argv,
197 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
204 throw std::runtime_error(
"failed to allocate memory for rcl context");
210 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to initialize rcl");
212 rcl_context_.reset(context, __delete_context);
215 logging_mutex_ = get_global_logging_mutex();
216 std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
217 size_t & count = get_logging_reference_count();
220 &rcl_context_->global_arguments,
222 rclcpp_logging_output_handler);
224 rcl_context_.reset();
225 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to configure logging");
230 "logging was initialized more than once");
236 std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
238 if (!unparsed_ros_arguments.empty()) {
242 init_options_ = init_options;
244 weak_contexts_ = get_weak_contexts();
245 weak_contexts_->add_context(this->shared_from_this());
246 }
catch (
const std::exception & e) {
248 rcl_context_.reset();
250 std::ostringstream oss;
251 oss <<
"While handling: " << e.what() << std::endl <<
252 " another exception was thrown";
253 rclcpp::exceptions::throw_from_rcl_error(ret, oss.str());
263 auto local_rcl_context = rcl_context_;
264 if (!local_rcl_context) {
273 return init_options_;
279 return init_options_;
288 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to get domain id from context");
296 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
297 return shutdown_reason_;
315 static thread_local std::unordered_set<const Context *> g_contexts_in_shutdown;
321 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
328 if (!g_contexts_in_shutdown.insert(
this).second) {
332 RCPPUTILS_SCOPE_EXIT(g_contexts_in_shutdown.erase(
this); );
336 std::lock_guard<std::mutex> lock{pre_shutdown_callbacks_mutex_};
337 for (
const auto & callback : pre_shutdown_callbacks_) {
345 rclcpp::exceptions::throw_from_rcl_error(ret);
348 shutdown_reason_ = reason;
351 std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
352 for (
const auto & callback : on_shutdown_callbacks_) {
360 weak_contexts_->remove_context(
this);
362 if (logging_mutex_) {
364 std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
365 size_t & count = get_logging_reference_count();
369 RCUTILS_SAFE_FWRITE_TO_STDERR(
370 RCUTILS_STRINGIFY(__file__)
":"
371 RCUTILS_STRINGIFY(__LINE__)
372 " failed to fini logging");
380 rclcpp::Context::OnShutdownCallback
390 return add_shutdown_callback(ShutdownType::on_shutdown, callback);
396 return remove_shutdown_callback(ShutdownType::on_shutdown, callback_handle);
402 return add_shutdown_callback(ShutdownType::pre_shutdown, callback);
409 return remove_shutdown_callback(ShutdownType::pre_shutdown, callback_handle);
413 Context::add_shutdown_callback(
414 ShutdownType shutdown_type,
415 ShutdownCallback callback)
417 auto callback_shared_ptr =
418 std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
420 switch (shutdown_type) {
421 case ShutdownType::pre_shutdown:
423 std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
424 pre_shutdown_callbacks_.emplace(callback_shared_ptr);
427 case ShutdownType::on_shutdown:
429 std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
430 on_shutdown_callbacks_.emplace(callback_shared_ptr);
435 ShutdownCallbackHandle callback_handle;
436 callback_handle.callback = callback_shared_ptr;
437 return callback_handle;
441 Context::remove_shutdown_callback(
442 ShutdownType shutdown_type,
443 const ShutdownCallbackHandle & callback_handle)
445 std::mutex * mutex_ptr =
nullptr;
447 std::shared_ptr<ShutdownCallbackHandle::ShutdownCallbackType>> * callback_list_ptr;
449 switch (shutdown_type) {
450 case ShutdownType::pre_shutdown:
451 mutex_ptr = &pre_shutdown_callbacks_mutex_;
452 callback_list_ptr = &pre_shutdown_callbacks_;
454 case ShutdownType::on_shutdown:
455 mutex_ptr = &on_shutdown_callbacks_mutex_;
456 callback_list_ptr = &on_shutdown_callbacks_;
460 std::lock_guard<std::mutex> lock(*mutex_ptr);
461 auto callback_shared_ptr = callback_handle.callback.lock();
462 if (callback_shared_ptr ==
nullptr) {
465 return callback_list_ptr->erase(callback_shared_ptr) == 1;
468 std::vector<rclcpp::Context::OnShutdownCallback>
471 return get_shutdown_callback(ShutdownType::on_shutdown);
474 std::vector<rclcpp::Context::PreShutdownCallback>
477 return get_shutdown_callback(ShutdownType::pre_shutdown);
480 std::vector<rclcpp::Context::ShutdownCallback>
481 Context::get_shutdown_callback(ShutdownType shutdown_type)
const
483 std::mutex * mutex_ptr =
nullptr;
484 const std::unordered_set<
485 std::shared_ptr<ShutdownCallbackHandle::ShutdownCallbackType>> * callback_list_ptr;
487 switch (shutdown_type) {
488 case ShutdownType::pre_shutdown:
489 mutex_ptr = &pre_shutdown_callbacks_mutex_;
490 callback_list_ptr = &pre_shutdown_callbacks_;
492 case ShutdownType::on_shutdown:
493 mutex_ptr = &on_shutdown_callbacks_mutex_;
494 callback_list_ptr = &on_shutdown_callbacks_;
498 std::vector<rclcpp::Context::ShutdownCallback> callbacks;
500 std::lock_guard<std::mutex> lock(*mutex_ptr);
501 for (
auto & iter : *callback_list_ptr) {
502 callbacks.emplace_back(*iter);
509 std::shared_ptr<rcl_context_t>
518 std::chrono::nanoseconds time_left = nanoseconds;
521 std::unique_lock<std::mutex> lock(interrupt_mutex_);
522 auto start = std::chrono::steady_clock::now();
524 interrupt_condition_variable_.wait_for(lock, time_left);
525 time_left -= std::chrono::steady_clock::now() - start;
527 }
while (time_left > std::chrono::nanoseconds::zero() && this->
is_valid());
535 interrupt_condition_variable_.notify_all();
541 shutdown_reason_ =
"";
542 rcl_context_.reset();
543 sub_contexts_.clear();
546 std::vector<Context::SharedPtr>
549 WeakContextsWrapper::SharedPtr weak_contexts = get_weak_contexts();
550 return weak_contexts->get_contexts();
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Thrown when init is called on an already initialized context.
Context which encapsulates shared state between nodes and other similar entities.
virtual RCLCPP_PUBLIC void init(int argc, char const *const *argv, const rclcpp::InitOptions &init_options=rclcpp::InitOptions())
Initialize the context, and the underlying elements like the rcl context.
RCLCPP_PUBLIC std::vector< OnShutdownCallback > get_on_shutdown_callbacks() const
Return the shutdown callbacks.
RCLCPP_PUBLIC std::vector< PreShutdownCallback > get_pre_shutdown_callbacks() const
Return the pre-shutdown callbacks.
RCLCPP_PUBLIC size_t get_domain_id() const
Return actual domain id.
RCLCPP_PUBLIC std::string shutdown_reason() const
Return the shutdown reason, or empty string if not shutdown.
RCLCPP_PUBLIC const rclcpp::InitOptions & get_init_options() const
Return the init options used during init.
RCLCPP_PUBLIC bool sleep_for(const std::chrono::nanoseconds &nanoseconds)
Sleep for a given period of time or until shutdown() is called.
virtual RCLCPP_PUBLIC void interrupt_all_sleep_for()
Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
virtual RCLCPP_PUBLIC OnShutdownCallback on_shutdown(OnShutdownCallback callback)
Add a on_shutdown callback to be called when shutdown is called for this context.
virtual RCLCPP_PUBLIC OnShutdownCallbackHandle add_on_shutdown_callback(OnShutdownCallback callback)
Add a on_shutdown callback to be called when shutdown is called for this context.
virtual RCLCPP_PUBLIC bool remove_pre_shutdown_callback(const PreShutdownCallbackHandle &callback_handle)
Remove an registered pre_shutdown callback.
RCLCPP_PUBLIC bool is_valid() const
Return true if the context is valid, otherwise false.
virtual RCLCPP_PUBLIC PreShutdownCallbackHandle add_pre_shutdown_callback(PreShutdownCallback callback)
Add a pre_shutdown callback to be called before shutdown is called for this context.
virtual RCLCPP_PUBLIC bool remove_on_shutdown_callback(const OnShutdownCallbackHandle &callback_handle)
Remove an registered on_shutdown callbacks.
virtual RCLCPP_PUBLIC bool shutdown(const std::string &reason)
Shutdown the context, making it uninitialized and therefore invalid for derived entities.
RCLCPP_PUBLIC std::shared_ptr< rcl_context_t > get_rcl_context()
Return the internal rcl context.
Encapsulation of options for initializing rclcpp.
RCLCPP_PUBLIC const rcl_init_options_t * get_rcl_init_options() const
Return the rcl init options.
RCLCPP_PUBLIC bool auto_initialize_logging() const
Return true if logging should be initialized when rclcpp::Context::init is called.
Class to manage vector of weak pointers to all created contexts.
Thrown when unparsed ROS specific arguments are found.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_context_fini(rcl_context_t *context)
Finalize a context.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_context_get_domain_id(rcl_context_t *context, size_t *domain_id)
Returns the context domain id.
struct rcl_context_s rcl_context_t
Encapsulates the non-global state of an init/shutdown cycle.
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_context_is_valid(const rcl_context_t *context)
Return true if the given context is currently valid, otherwise false.
RCL_PUBLIC RCL_WARN_UNUSED rcl_context_t rcl_get_zero_initialized_context(void)
Return a zero initialization context object.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_shutdown(rcl_context_t *context)
Shutdown a given rcl context.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_init(int argc, char const *const *argv, const rcl_init_options_t *options, rcl_context_t *context)
Initialization of rcl.
RCL_PUBLIC RCL_WARN_UNUSED const rcl_allocator_t * rcl_init_options_get_allocator(const rcl_init_options_t *init_options)
Return the allocator stored in the init_options.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_fini(void)
RCL_PUBLIC void rcl_logging_multiple_output_handler(const rcutils_log_location_t *location, int severity, const char *name, rcutils_time_point_value_t timestamp, const char *format, va_list *args)
Default output handler used by rcl.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_configure_with_output_handler(const rcl_arguments_t *global_args, const rcl_allocator_t *allocator, rcl_logging_output_handler_t output_handler)
Configure the logging system with the provided output handler.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::vector< Context::SharedPtr > get_contexts()
Return a copy of the list of context shared pointers.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Encapsulates the non-global state of an init/shutdown cycle.
#define RCL_RET_OK
Success return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.