ROS 2 rclcpp + rcl - jazzy  jazzy
ROS 2 C++ Client Library with ROS Client Library
context.cpp
1 // Copyright 2015-2020 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rclcpp/context.hpp"
16 
17 #include <map>
18 #include <memory>
19 #include <mutex>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 #include <unordered_set>
24 #include <utility>
25 
26 #include "rcl/init.h"
27 #include "rcl/logging.h"
28 
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"
36 
37 #include "./logging_mutex.hpp"
38 
39 using rclcpp::Context;
40 
41 namespace rclcpp
42 {
45 {
46 public:
47  RCLCPP_SMART_PTR_DEFINITIONS(WeakContextsWrapper)
48 
49  void
50  add_context(const Context::SharedPtr & context)
51  {
52  std::lock_guard<std::mutex> guard(mutex_);
53  weak_contexts_.push_back(context);
54  }
55 
56  void
57  remove_context(const Context * context)
58  {
59  std::lock_guard<std::mutex> guard(mutex_);
60  weak_contexts_.erase(
61  std::remove_if(
62  weak_contexts_.begin(),
63  weak_contexts_.end(),
64  [context](const Context::WeakPtr weak_context) {
65  auto locked_context = weak_context.lock();
66  if (!locked_context) {
67  // take advantage and removed expired contexts
68  return true;
69  }
70  return locked_context.get() == context;
71  }
72  ),
73  weak_contexts_.end());
74  }
75 
76  std::vector<Context::SharedPtr>
77  get_contexts()
78  {
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(); /* noop */) {
82  auto context_ptr = it->lock();
83  if (!context_ptr) {
84  // remove invalid weak context pointers
85  it = weak_contexts_.erase(it);
86  } else {
87  ++it;
88  shared_contexts.push_back(context_ptr);
89  }
90  }
91  return shared_contexts;
92  }
93 
94 private:
95  std::vector<std::weak_ptr<rclcpp::Context>> weak_contexts_;
96  std::mutex mutex_;
97 };
98 } // namespace rclcpp
99 
101 
103 static
104 WeakContextsWrapper::SharedPtr
105 get_weak_contexts()
106 {
107  static WeakContextsWrapper::SharedPtr weak_contexts = WeakContextsWrapper::make_shared();
108  if (!weak_contexts) {
109  throw std::runtime_error("weak contexts vector is not valid");
110  }
111  return weak_contexts;
112 }
113 
115 static
116 size_t &
117 get_logging_reference_count()
118 {
119  static size_t ref_count = 0;
120  return ref_count;
121 }
122 
123 extern "C"
124 {
125 static
126 void
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)
131 {
132  try {
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");
141  } catch (...) {
142  RCUTILS_SAFE_FWRITE_TO_STDERR("failed to take global rclcpp logging mutex\n");
143  }
144 }
145 } // extern "C"
146 
152 {
153  std::mutex m;
154 
155  struct MutexHolder
156  {
157  std::recursive_mutex on_shutdown_callbacks_mutex_;
158  std::recursive_mutex pre_shutdown_callbacks_mutex_;
159  };
160 
161  std::map<const Context *, std::unique_ptr<MutexHolder>> mutexMap;
162 
163 public:
164  MutexHolder & getMutexes(const Context *forContext)
165  {
166  auto it = mutexMap.find(forContext);
167  if(it == mutexMap.end()) {
168  it = mutexMap.emplace(forContext, std::make_unique<MutexHolder>()).first;
169  }
170 
171  return *(it->second);
172  }
173 
177  void removeMutexes(const Context *forContext)
178  {
179  mutexMap.erase(forContext);
180  }
181 };
182 
183 MutexLookup mutexStorage;
184 
185 Context::Context()
186 : rcl_context_(nullptr),
187  shutdown_reason_(""),
188  logging_mutex_(nullptr)
189 {
190  // allocate mutexes
191  mutexStorage.getMutexes(this);
192 }
193 
194 Context::~Context()
195 {
196  // acquire the init lock to prevent race conditions with init and shutdown
197  // this will not prevent errors, but will maybe make them easier to reproduce
198  std::lock_guard<std::recursive_mutex> lock(init_mutex_);
199  try {
200  // Cannot rely on virtual dispatch in a destructor, so explicitly use the
201  // shutdown() provided by this base class.
202  Context::shutdown("context destructor was called while still not shutdown");
203  // at this point it is shutdown and cannot reinit
204  // clean_up will finalize the rcl context
205  this->clean_up();
206  } catch (const std::exception & exc) {
207  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context(): %s", exc.what());
208  } catch (...) {
209  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context()");
210  }
211 
212  // delete mutexes
213  mutexStorage.removeMutexes(this);
214 }
215 
216 RCLCPP_LOCAL
217 void
218 __delete_context(rcl_context_t * context)
219 {
220  if (context) {
221  if (rcl_context_is_valid(context)) {
222  RCLCPP_ERROR(
223  rclcpp::get_logger("rclcpp"), "rcl context unexpectedly not shutdown during cleanup");
224  } else {
225  // if context pointer is not null and is shutdown, then it's ready for fini
226  rcl_ret_t ret = rcl_context_fini(context);
227  if (RCL_RET_OK != ret) {
228  RCLCPP_ERROR(
229  rclcpp::get_logger("rclcpp"),
230  "failed to finalize context: %s", rcl_get_error_string().str);
231  rcl_reset_error();
232  }
233  }
234  delete context;
235  }
236 }
237 
238 void
240  int argc,
241  char const * const * argv,
242  const rclcpp::InitOptions & init_options)
243 {
244  std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
245  if (this->is_valid()) {
247  }
248  this->clean_up();
249  rcl_context_t * context = new rcl_context_t;
250  if (!context) {
251  throw std::runtime_error("failed to allocate memory for rcl context");
252  }
254  rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), context);
255  if (RCL_RET_OK != ret) {
256  delete context;
257  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialize rcl");
258  }
259  rcl_context_.reset(context, __delete_context);
260 
261  if (init_options.auto_initialize_logging()) {
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();
265  if (0u == count) {
267  &rcl_context_->global_arguments,
269  rclcpp_logging_output_handler);
270  if (RCL_RET_OK != ret) {
271  rcl_context_.reset();
272  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
273  }
274  } else {
275  RCLCPP_WARN(
276  rclcpp::get_logger("rclcpp"),
277  "logging was initialized more than once");
278  }
279  ++count;
280  }
281 
282  try {
283  std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
284  argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
285  if (!unparsed_ros_arguments.empty()) {
286  throw exceptions::UnknownROSArgsError(std::move(unparsed_ros_arguments));
287  }
288 
289  init_options_ = init_options;
290 
291  weak_contexts_ = get_weak_contexts();
292  weak_contexts_->add_context(this->shared_from_this());
293  } catch (const std::exception & e) {
294  ret = rcl_shutdown(rcl_context_.get());
295  rcl_context_.reset();
296  if (RCL_RET_OK != ret) {
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());
301  }
302  throw;
303  }
304 }
305 
306 bool
308 {
309  // Take a local copy of the shared pointer to avoid it getting nulled under our feet.
310  auto local_rcl_context = rcl_context_;
311  if (!local_rcl_context) {
312  return false;
313  }
314  return rcl_context_is_valid(local_rcl_context.get());
315 }
316 
317 const rclcpp::InitOptions &
319 {
320  return init_options_;
321 }
322 
325 {
326  return init_options_;
327 }
328 
329 size_t
331 {
332  size_t domain_id;
333  rcl_ret_t ret = rcl_context_get_domain_id(rcl_context_.get(), &domain_id);
334  if (RCL_RET_OK != ret) {
335  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to get domain id from context");
336  }
337  return domain_id;
338 }
339 
340 std::string
342 {
343  std::lock_guard<std::recursive_mutex> lock(init_mutex_);
344  return shutdown_reason_;
345 }
346 
348 
362 static thread_local std::unordered_set<const Context *> g_contexts_in_shutdown;
363 
364 bool
365 Context::shutdown(const std::string & reason)
366 {
367  // prevent races
368  std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
369  // ensure validity
370  if (!this->is_valid()) {
371  // if it is not valid, then it cannot be shutdown
372  return false;
373  }
374  // prevent reentrant calls, e.g. from a pre_shutdown callback
375  if (!g_contexts_in_shutdown.insert(this).second) {
376  // shutdown of this context is already in progress on this thread
377  return false;
378  }
379  RCPPUTILS_SCOPE_EXIT(g_contexts_in_shutdown.erase(this); );
380 
381  // call each pre-shutdown callback
382  {
383  std::lock_guard<std::recursive_mutex> lock{mutexStorage.getMutexes(
384  this).pre_shutdown_callbacks_mutex_};
385  // callbacks may delete other callbacks during the execution,
386  // therefore we need to save a copy and check before execution
387  // if the next callback is still present
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()) {
392  (*callback)();
393  }
394  }
395  }
396 
397  // rcl shutdown
398  rcl_ret_t ret = rcl_shutdown(rcl_context_.get());
399  if (RCL_RET_OK != ret) {
400  rclcpp::exceptions::throw_from_rcl_error(ret);
401  }
402  // set shutdown reason
403  shutdown_reason_ = reason;
404  // call each shutdown callback
405  {
406  std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
407  this).on_shutdown_callbacks_mutex_);
408  // callbacks may delete other callbacks during the execution,
409  // therefore we need to save a copy and check before execution
410  // if the next callback is still present
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()) {
415  (*callback)();
416  }
417  }
418  }
419 
420  // interrupt all blocking sleep_for() and all blocking executors or wait sets
421  this->interrupt_all_sleep_for();
422  // remove self from the global contexts
423  weak_contexts_->remove_context(this);
424  // shutdown logger
425  if (logging_mutex_) {
426  // logging was initialized by this context
427  std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
428  size_t & count = get_logging_reference_count();
429  if (0u == --count) {
430  rcl_ret_t rcl_ret = rcl_logging_fini();
431  if (RCL_RET_OK != rcl_ret) {
432  RCUTILS_SAFE_FWRITE_TO_STDERR(
433  RCUTILS_STRINGIFY(__file__) ":"
434  RCUTILS_STRINGIFY(__LINE__)
435  " failed to fini logging");
436  rcl_reset_error();
437  }
438  }
439  }
440  return true;
441 }
442 
443 rclcpp::Context::OnShutdownCallback
444 Context::on_shutdown(OnShutdownCallback callback)
445 {
446  add_on_shutdown_callback(callback);
447  return callback;
448 }
449 
451 Context::add_on_shutdown_callback(OnShutdownCallback callback)
452 {
453  return add_shutdown_callback<ShutdownType::on_shutdown>(callback);
454 }
455 
456 bool
458 {
459  return remove_shutdown_callback<ShutdownType::on_shutdown>(callback_handle);
460 }
461 
463 Context::add_pre_shutdown_callback(PreShutdownCallback callback)
464 {
465  return add_shutdown_callback<ShutdownType::pre_shutdown>(callback);
466 }
467 
468 bool
470  const PreShutdownCallbackHandle & callback_handle)
471 {
472  return remove_shutdown_callback<ShutdownType::pre_shutdown>(callback_handle);
473 }
474 
475 template<Context::ShutdownType shutdown_type>
477 Context::add_shutdown_callback(
478  ShutdownCallback callback)
479 {
480  auto callback_shared_ptr =
481  std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
482 
483  static_assert(
484  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
485 
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);
490  } else {
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);
494  }
495 
496  ShutdownCallbackHandle callback_handle;
497  callback_handle.callback = callback_shared_ptr;
498  return callback_handle;
499 }
500 
501 template<Context::ShutdownType shutdown_type>
502 bool
503 Context::remove_shutdown_callback(
504  const ShutdownCallbackHandle & callback_handle)
505 {
506  const auto callback_shared_ptr = callback_handle.callback.lock();
507  if (callback_shared_ptr == nullptr) {
508  return false;
509  }
510 
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()) {
516  break;
517  }
518  }
519  if (iter == callback_vector.end()) {
520  return false;
521  }
522  callback_vector.erase(iter);
523 
524  return true;
525  };
526 
527  static_assert(
528  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
529 
530  if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
531  return remove_callback(mutexStorage.getMutexes(this).pre_shutdown_callbacks_mutex_,
532  pre_shutdown_callbacks_);
533  } else {
534  return remove_callback(mutexStorage.getMutexes(this).on_shutdown_callbacks_mutex_,
535  on_shutdown_callbacks_);
536  }
537 }
538 
539 std::vector<rclcpp::Context::OnShutdownCallback>
541 {
542  return get_shutdown_callback<ShutdownType::on_shutdown>();
543 }
544 
545 std::vector<rclcpp::Context::PreShutdownCallback>
547 {
548  return get_shutdown_callback<ShutdownType::pre_shutdown>();
549 }
550 
551 template<Context::ShutdownType shutdown_type>
552 std::vector<rclcpp::Context::ShutdownCallback>
553 Context::get_shutdown_callback() const
554 {
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);
560  }
561  return callbacks;
562  };
563 
564  static_assert(
565  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
566 
567  if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
568  return get_callback_vector(mutexStorage.getMutexes(this).pre_shutdown_callbacks_mutex_,
569  pre_shutdown_callbacks_);
570  } else {
571  return get_callback_vector(mutexStorage.getMutexes(this).on_shutdown_callbacks_mutex_,
572  on_shutdown_callbacks_);
573  }
574 }
575 
576 std::shared_ptr<rcl_context_t>
578 {
579  return rcl_context_;
580 }
581 
582 bool
583 Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
584 {
585  std::chrono::nanoseconds time_left = nanoseconds;
586  do {
587  {
588  std::unique_lock<std::mutex> lock(interrupt_mutex_);
589  auto start = std::chrono::steady_clock::now();
590  // this will release the lock while waiting
591  interrupt_condition_variable_.wait_for(lock, time_left);
592  time_left -= std::chrono::steady_clock::now() - start;
593  }
594  } while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());
595  // Return true if the timeout elapsed successfully, otherwise false.
596  return this->is_valid();
597 }
598 
599 void
601 {
602  interrupt_condition_variable_.notify_all();
603 }
604 
605 void
606 Context::clean_up()
607 {
608  shutdown_reason_ = "";
609  rcl_context_.reset();
610  sub_contexts_.clear();
611 }
612 
613 std::vector<Context::SharedPtr>
615 {
616  WeakContextsWrapper::SharedPtr weak_contexts = get_weak_contexts();
617  return weak_contexts->get_contexts();
618 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
void removeMutexes(const Context *forContext)
Definition: context.cpp:177
Thrown when init is called on an already initialized context.
Definition: context.hpp:43
Context which encapsulates shared state between nodes and other similar entities.
Definition: context.hpp:76
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.
Definition: context.cpp:239
RCLCPP_PUBLIC std::vector< OnShutdownCallback > get_on_shutdown_callbacks() const
Return the shutdown callbacks.
Definition: context.cpp:540
RCLCPP_PUBLIC std::vector< PreShutdownCallback > get_pre_shutdown_callbacks() const
Return the pre-shutdown callbacks.
Definition: context.cpp:546
RCLCPP_PUBLIC size_t get_domain_id() const
Return actual domain id.
Definition: context.cpp:330
RCLCPP_PUBLIC std::string shutdown_reason() const
Return the shutdown reason, or empty string if not shutdown.
Definition: context.cpp:341
RCLCPP_PUBLIC const rclcpp::InitOptions & get_init_options() const
Return the init options used during init.
Definition: context.cpp:318
RCLCPP_PUBLIC bool sleep_for(const std::chrono::nanoseconds &nanoseconds)
Sleep for a given period of time or until shutdown() is called.
Definition: context.cpp:583
RCLCPP_PUBLIC void interrupt_all_sleep_for()
Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
Definition: context.cpp:600
virtual RCLCPP_PUBLIC OnShutdownCallback on_shutdown(OnShutdownCallback callback)
Add a on_shutdown callback to be called when shutdown is called for this context.
Definition: context.cpp:444
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.
Definition: context.cpp:451
virtual RCLCPP_PUBLIC bool remove_pre_shutdown_callback(const PreShutdownCallbackHandle &callback_handle)
Remove an registered pre_shutdown callback.
Definition: context.cpp:469
RCLCPP_PUBLIC bool is_valid() const
Return true if the context is valid, otherwise false.
Definition: context.cpp:307
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.
Definition: context.cpp:463
virtual RCLCPP_PUBLIC bool remove_on_shutdown_callback(const OnShutdownCallbackHandle &callback_handle)
Remove an registered on_shutdown callbacks.
Definition: context.cpp:457
virtual RCLCPP_PUBLIC bool shutdown(const std::string &reason)
Shutdown the context, making it uninitialized and therefore invalid for derived entities.
Definition: context.cpp:365
RCLCPP_PUBLIC std::shared_ptr< rcl_context_t > get_rcl_context()
Return the internal rcl context.
Definition: context.cpp:577
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.
Definition: context.cpp:45
Thrown when unparsed ROS specific arguments are found.
Definition: exceptions.hpp:197
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_context_fini(rcl_context_t *context)
Finalize a context.
Definition: context.c:49
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.
Definition: context.c:83
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.
Definition: context.c:94
RCL_PUBLIC RCL_WARN_UNUSED rcl_context_t rcl_get_zero_initialized_context(void)
Return a zero initialization context object.
Definition: context.c:29
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_shutdown(rcl_context_t *context)
Shutdown a given rcl context.
Definition: init.c:324
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.
Definition: init.c:47
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.
Definition: init_options.c:175
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_fini(void)
Definition: logging.c:127
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.
Definition: logging.c:154
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.
Definition: logging.c:57
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.
Definition: context.cpp:614
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:33
Encapsulates the non-global state of an init/shutdown cycle.
Definition: context.h:114
#define RCL_RET_OK
Success return code.
Definition: types.h:27
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24