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"
32 #include "rclcpp/graph_listener.hpp"
33 #include "rcpputils/scope_exit.hpp"
34 #include "rcutils/error_handling.h"
35 #include "rcutils/macros.h"
37 #include "./logging_mutex.hpp"
50 add_context(
const Context::SharedPtr & context)
52 std::lock_guard<std::mutex> guard(mutex_);
53 weak_contexts_.push_back(context);
57 remove_context(
const Context * context)
59 std::lock_guard<std::mutex> guard(mutex_);
62 weak_contexts_.begin(),
64 [context](
const Context::WeakPtr weak_context) {
65 auto locked_context = weak_context.lock();
66 if (!locked_context) {
70 return locked_context.get() == context;
73 weak_contexts_.end());
76 std::vector<Context::SharedPtr>
79 std::lock_guard<std::mutex> lock(mutex_);
80 std::vector<Context::SharedPtr> shared_contexts;
81 for (
auto it = weak_contexts_.begin(); it != weak_contexts_.end(); ) {
82 auto context_ptr = it->lock();
85 it = weak_contexts_.erase(it);
88 shared_contexts.push_back(context_ptr);
91 return shared_contexts;
95 std::vector<std::weak_ptr<rclcpp::Context>> weak_contexts_;
104 WeakContextsWrapper::SharedPtr
107 static WeakContextsWrapper::SharedPtr weak_contexts = WeakContextsWrapper::make_shared();
108 if (!weak_contexts) {
109 throw std::runtime_error(
"weak contexts vector is not valid");
111 return weak_contexts;
117 get_logging_reference_count()
119 static size_t ref_count = 0;
127 rclcpp_logging_output_handler(
128 const rcutils_log_location_t * location,
129 int severity,
const char * name, rcutils_time_point_value_t timestamp,
130 const char * format, va_list * args)
133 std::shared_ptr<std::recursive_mutex> logging_mutex;
134 logging_mutex = get_global_logging_mutex();
135 std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
137 location, severity, name, timestamp, format, args);
138 }
catch (std::exception & ex) {
139 RCUTILS_SAFE_FWRITE_TO_STDERR(ex.what());
140 RCUTILS_SAFE_FWRITE_TO_STDERR(
"\n");
142 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to take global rclcpp logging mutex\n");
157 std::recursive_mutex on_shutdown_callbacks_mutex_;
158 std::recursive_mutex pre_shutdown_callbacks_mutex_;
161 std::map<const Context *, std::unique_ptr<MutexHolder>> mutexMap;
164 MutexHolder & getMutexes(
const Context *forContext)
166 auto it = mutexMap.find(forContext);
167 if(it == mutexMap.end()) {
168 it = mutexMap.emplace(forContext, std::make_unique<MutexHolder>()).first;
171 return *(it->second);
179 mutexMap.erase(forContext);
186 : rcl_context_(nullptr),
187 shutdown_reason_(
""),
188 logging_mutex_(nullptr)
191 mutexStorage.getMutexes(
this);
198 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
206 }
catch (
const std::exception & exc) {
207 RCLCPP_ERROR(
rclcpp::get_logger(
"rclcpp"),
"unhandled exception in ~Context(): %s", exc.what());
223 rclcpp::get_logger(
"rclcpp"),
"rcl context unexpectedly not shutdown during cleanup");
230 "failed to finalize context: %s", rcl_get_error_string().str);
241 char const *
const * argv,
244 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
251 throw std::runtime_error(
"failed to allocate memory for rcl context");
257 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to initialize rcl");
259 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 rcl_context_.reset();
272 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to configure logging");
277 "logging was initialized more than once");
283 std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
285 if (!unparsed_ros_arguments.empty()) {
289 init_options_ = init_options;
291 weak_contexts_ = get_weak_contexts();
292 weak_contexts_->add_context(this->shared_from_this());
293 }
catch (
const std::exception & e) {
295 rcl_context_.reset();
297 std::ostringstream oss;
298 oss <<
"While handling: " << e.what() << std::endl <<
299 " another exception was thrown";
300 rclcpp::exceptions::throw_from_rcl_error(ret, oss.str());
310 auto local_rcl_context = rcl_context_;
311 if (!local_rcl_context) {
320 return init_options_;
326 return init_options_;
335 rclcpp::exceptions::throw_from_rcl_error(ret,
"failed to get domain id from context");
343 std::lock_guard<std::recursive_mutex> lock(init_mutex_);
344 return shutdown_reason_;
362 static thread_local std::unordered_set<const Context *> g_contexts_in_shutdown;
368 std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
375 if (!g_contexts_in_shutdown.insert(
this).second) {
379 RCPPUTILS_SCOPE_EXIT(g_contexts_in_shutdown.erase(
this); );
383 std::lock_guard<std::recursive_mutex> lock{mutexStorage.getMutexes(
384 this).pre_shutdown_callbacks_mutex_};
388 auto cpy = pre_shutdown_callbacks_;
389 for (
const auto & callback : cpy) {
390 auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
391 if(it != pre_shutdown_callbacks_.end()) {
400 rclcpp::exceptions::throw_from_rcl_error(ret);
403 shutdown_reason_ = reason;
406 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
407 this).on_shutdown_callbacks_mutex_);
411 auto cpy = on_shutdown_callbacks_;
412 for (
const auto & callback : cpy) {
413 auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
414 if(it != on_shutdown_callbacks_.end()) {
423 weak_contexts_->remove_context(
this);
425 if (logging_mutex_) {
427 std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
428 size_t & count = get_logging_reference_count();
432 RCUTILS_SAFE_FWRITE_TO_STDERR(
433 RCUTILS_STRINGIFY(__file__)
":"
434 RCUTILS_STRINGIFY(__LINE__)
435 " failed to fini logging");
443 rclcpp::Context::OnShutdownCallback
453 return add_shutdown_callback<ShutdownType::on_shutdown>(callback);
459 return remove_shutdown_callback<ShutdownType::on_shutdown>(callback_handle);
465 return add_shutdown_callback<ShutdownType::pre_shutdown>(callback);
472 return remove_shutdown_callback<ShutdownType::pre_shutdown>(callback_handle);
475 template<Context::ShutdownType shutdown_type>
477 Context::add_shutdown_callback(
478 ShutdownCallback callback)
480 auto callback_shared_ptr =
481 std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
484 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
486 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
487 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
488 this).pre_shutdown_callbacks_mutex_);
489 pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
491 std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
492 this).on_shutdown_callbacks_mutex_);
493 on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
496 ShutdownCallbackHandle callback_handle;
497 callback_handle.callback = callback_shared_ptr;
498 return callback_handle;
501 template<Context::ShutdownType shutdown_type>
503 Context::remove_shutdown_callback(
504 const ShutdownCallbackHandle & callback_handle)
506 const auto callback_shared_ptr = callback_handle.callback.lock();
507 if (callback_shared_ptr ==
nullptr) {
511 const auto remove_callback = [&callback_shared_ptr](
auto & mutex,
auto & callback_vector) {
512 const std::lock_guard<std::recursive_mutex> lock(mutex);
513 auto iter = callback_vector.begin();
514 for (; iter != callback_vector.end(); iter++) {
515 if ((*iter).get() == callback_shared_ptr.get()) {
519 if (iter == callback_vector.end()) {
522 callback_vector.erase(iter);
528 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
530 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
531 return remove_callback(mutexStorage.getMutexes(
this).pre_shutdown_callbacks_mutex_,
532 pre_shutdown_callbacks_);
534 return remove_callback(mutexStorage.getMutexes(
this).on_shutdown_callbacks_mutex_,
535 on_shutdown_callbacks_);
539 std::vector<rclcpp::Context::OnShutdownCallback>
542 return get_shutdown_callback<ShutdownType::on_shutdown>();
545 std::vector<rclcpp::Context::PreShutdownCallback>
548 return get_shutdown_callback<ShutdownType::pre_shutdown>();
551 template<Context::ShutdownType shutdown_type>
552 std::vector<rclcpp::Context::ShutdownCallback>
553 Context::get_shutdown_callback()
const
555 const auto get_callback_vector = [](
auto & mutex,
auto & callback_set) {
556 const std::lock_guard<std::recursive_mutex> lock(mutex);
557 std::vector<rclcpp::Context::ShutdownCallback> callbacks;
558 for (
auto & callback : callback_set) {
559 callbacks.push_back(*callback);
565 shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
567 if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
568 return get_callback_vector(mutexStorage.getMutexes(
this).pre_shutdown_callbacks_mutex_,
569 pre_shutdown_callbacks_);
571 return get_callback_vector(mutexStorage.getMutexes(
this).on_shutdown_callbacks_mutex_,
572 on_shutdown_callbacks_);
576 std::shared_ptr<rcl_context_t>
585 std::chrono::nanoseconds time_left = nanoseconds;
588 std::unique_lock<std::mutex> lock(interrupt_mutex_);
589 auto start = std::chrono::steady_clock::now();
591 interrupt_condition_variable_.wait_for(lock, time_left);
592 time_left -= std::chrono::steady_clock::now() - start;
594 }
while (time_left > std::chrono::nanoseconds::zero() && this->
is_valid());
602 interrupt_condition_variable_.notify_all();
608 shutdown_reason_ =
"";
609 rcl_context_.reset();
610 sub_contexts_.clear();
613 std::vector<Context::SharedPtr>
616 WeakContextsWrapper::SharedPtr weak_contexts = get_weak_contexts();
617 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.