15 #include "rclcpp/context.hpp"
23 #include <unordered_set>
29 #include "rclcpp/detail/utilities.hpp"
30 #include "rclcpp/exceptions.hpp"
31 #include "rclcpp/logging.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");
156 std::recursive_mutex on_shutdown_callbacks_mutex_;
157 std::recursive_mutex pre_shutdown_callbacks_mutex_;
160 std::map<const Context *, std::unique_ptr<MutexHolder>> mutexMap;
163 MutexHolder & getMutexes(
const Context *forContext)
165 auto it = mutexMap.find(forContext);
166 if(it == mutexMap.end()) {
167 it = mutexMap.emplace(forContext, std::make_unique<MutexHolder>()).first;
170 return *(it->second);
178 mutexMap.erase(forContext);
185 : rcl_context_(nullptr),
186 shutdown_reason_(
""),
187 logging_mutex_(nullptr)
190 mutexStorage.getMutexes(
this);
197 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
205 }
catch (
const std::exception & exc) {
206 RCLCPP_ERROR(
rclcpp::get_logger(
"rclcpp"),
"unhandled exception in ~Context(): %s", exc.what());
222 rclcpp::get_logger(
"rclcpp"),
"rcl context unexpectedly not shutdown during cleanup");
229 "failed to finalize context: %s", rcl_get_error_string().str);
240 char const *
const * argv,
243 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
250 throw std::runtime_error(
"failed to allocate memory for rcl context");
256 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to initialize rcl");
258 rcl_context_.reset(context, __delete_context);
262 logging_mutex_ = get_global_logging_mutex();
263 std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
264 size_t & count = get_logging_reference_count();
267 &rcl_context_->global_arguments,
269 rclcpp_logging_output_handler);
271 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to configure logging");
276 "logging was initialized more than once");
281 std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
283 if (!unparsed_ros_arguments.empty()) {
287 init_options_ = init_options;
289 weak_contexts_ = get_weak_contexts();
290 weak_contexts_->add_context(this->shared_from_this());
291 }
catch (
const std::exception & e) {
293 rcl_context_.reset();
295 std::ostringstream oss;
296 oss <<
"While handling: " << e.what() << std::endl <<
297 " another exception was thrown";
298 rclcpp::exceptions::throw_from_rcl_error(ret, oss.str());
308 auto local_rcl_context = rcl_context_;
309 if (!local_rcl_context) {
318 return init_options_;
324 return init_options_;
333 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to get domain id from context");
341 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
342 return shutdown_reason_;
349 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
358 std::lock_guard<std::recursive_mutex> lock{mutexStorage.getMutexes(
359 this).pre_shutdown_callbacks_mutex_};
363 auto cpy = pre_shutdown_callbacks_;
364 for (
const auto & callback : cpy) {
365 auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
366 if(it != pre_shutdown_callbacks_.end()) {
375 rclcpp::exceptions::throw_from_rcl_error(ret);
378 shutdown_reason_ = reason;
381 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
382 this).on_shutdown_callbacks_mutex_);
386 auto cpy = on_shutdown_callbacks_;
387 for (
const auto & callback : cpy) {
388 auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
389 if(it != on_shutdown_callbacks_.end()) {
398 weak_contexts_->remove_context(
this);
400 if (logging_mutex_) {
402 std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
403 size_t & count = get_logging_reference_count();
407 RCUTILS_SAFE_FWRITE_TO_STDERR(
408 RCUTILS_STRINGIFY(__file__)
":"
409 RCUTILS_STRINGIFY(__LINE__)
410 " failed to fini logging");
418 rclcpp::Context::OnShutdownCallback
428 return add_shutdown_callback<ShutdownType::on_shutdown>(callback);
434 return remove_shutdown_callback<ShutdownType::on_shutdown>(callback_handle);
440 return add_shutdown_callback<ShutdownType::pre_shutdown>(callback);
447 return remove_shutdown_callback<ShutdownType::pre_shutdown>(callback_handle);
450 template<Context::ShutdownType shutdown_type>
452 Context::add_shutdown_callback(
453 ShutdownCallback callback)
455 auto callback_shared_ptr =
456 std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
459 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
461 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
462 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
463 this).pre_shutdown_callbacks_mutex_);
464 pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
466 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
467 this).on_shutdown_callbacks_mutex_);
468 on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
471 ShutdownCallbackHandle callback_handle;
472 callback_handle.callback = callback_shared_ptr;
473 return callback_handle;
476 template<Context::ShutdownType shutdown_type>
478 Context::remove_shutdown_callback(
479 const ShutdownCallbackHandle & callback_handle)
481 const auto callback_shared_ptr = callback_handle.callback.lock();
482 if (callback_shared_ptr ==
nullptr) {
486 const auto remove_callback = [&callback_shared_ptr](
auto & mutex,
auto & callback_vector) {
487 const std::lock_guard<std::recursive_mutex> lock(mutex);
488 auto iter = callback_vector.begin();
489 for (; iter != callback_vector.end(); iter++) {
490 if ((*iter).get() == callback_shared_ptr.get()) {
494 if (iter == callback_vector.end()) {
497 callback_vector.erase(iter);
503 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
505 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
506 return remove_callback(mutexStorage.getMutexes(
this).pre_shutdown_callbacks_mutex_,
507 pre_shutdown_callbacks_);
509 return remove_callback(mutexStorage.getMutexes(
this).on_shutdown_callbacks_mutex_,
510 on_shutdown_callbacks_);
514 std::vector<rclcpp::Context::OnShutdownCallback>
517 return get_shutdown_callback<ShutdownType::on_shutdown>();
520 std::vector<rclcpp::Context::PreShutdownCallback>
523 return get_shutdown_callback<ShutdownType::pre_shutdown>();
526 template<Context::ShutdownType shutdown_type>
527 std::vector<rclcpp::Context::ShutdownCallback>
528 Context::get_shutdown_callback()
const
530 const auto get_callback_vector = [](
auto & mutex,
auto & callback_set) {
531 const std::lock_guard<std::recursive_mutex> lock(mutex);
532 std::vector<rclcpp::Context::ShutdownCallback> callbacks;
533 for (
auto & callback : callback_set) {
534 callbacks.push_back(*callback);
540 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
542 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
543 return get_callback_vector(mutexStorage.getMutexes(
this).pre_shutdown_callbacks_mutex_,
544 pre_shutdown_callbacks_);
546 return get_callback_vector(mutexStorage.getMutexes(
this).on_shutdown_callbacks_mutex_,
547 on_shutdown_callbacks_);
551 std::shared_ptr<rcl_context_t>
560 std::chrono::nanoseconds time_left = nanoseconds;
563 std::unique_lock<std::mutex> lock(interrupt_mutex_);
564 auto start = std::chrono::steady_clock::now();
566 interrupt_condition_variable_.wait_for(lock, time_left);
567 time_left -= std::chrono::steady_clock::now() - start;
569 }
while (time_left > std::chrono::nanoseconds::zero() && this->
is_valid());
577 interrupt_condition_variable_.notify_all();
583 shutdown_reason_ =
"";
584 rcl_context_.reset();
585 sub_contexts_.clear();
588 std::vector<Context::SharedPtr>
591 WeakContextsWrapper::SharedPtr weak_contexts = get_weak_contexts();
592 return weak_contexts->get_contexts();
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
void removeMutexes(const Context *forContext)
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.
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.