ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
executor.cpp
1 // Copyright 2015 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 <algorithm>
16 #include <cassert>
17 #include <chrono>
18 #include <iterator>
19 #include <memory>
20 #include <map>
21 #include <stdexcept>
22 #include <string>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26 
27 #include "rcl/allocator.h"
28 #include "rcl/error_handling.h"
29 #include "rclcpp/executors/executor_notify_waitable.hpp"
30 #include "rclcpp/subscription_wait_set_mask.hpp"
31 #include "rcpputils/scope_exit.hpp"
32 
33 #include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
34 #include "rclcpp/exceptions.hpp"
35 #include "rclcpp/executor.hpp"
36 #include "rclcpp/guard_condition.hpp"
37 #include "rclcpp/node.hpp"
38 #include "rclcpp/utilities.hpp"
39 
40 #include "rcutils/logging_macros.h"
41 
42 #include "tracetools/tracetools.h"
43 
44 using namespace std::chrono_literals;
45 
46 using rclcpp::Executor;
47 
50 static constexpr rclcpp::SubscriptionWaitSetMask kDefaultSubscriptionMask = {true, false, false};
51 
53 
54 Executor::Executor(const std::shared_ptr<rclcpp::Context> & context)
55 : spinning(false),
56  context_(context),
57  entities_need_rebuild_(true),
58  collector_(nullptr),
59  wait_set_({}, {}, {}, {}, {}, {}, context)
60 {
61 }
62 
64 : spinning(false),
65  interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
66  shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
67  context_(options.context),
68  notify_waitable_(std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
69  [this]() {
70  this->entities_need_rebuild_.store(true);
71  }, options.context)),
72  entities_need_rebuild_(true),
73  collector_(notify_waitable_),
74  wait_set_({}, {}, {}, {}, {}, {}, options.context),
75  current_notify_waitable_(notify_waitable_),
76  impl_(std::make_unique<rclcpp::ExecutorImplementation>())
77 {
78  shutdown_callback_handle_ = context_->add_on_shutdown_callback(
79  [weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {
80  auto strong_gc = weak_gc.lock();
81  if (strong_gc) {
82  strong_gc->trigger();
83  }
84  });
85 
86  notify_waitable_->add_guard_condition(interrupt_guard_condition_);
87  notify_waitable_->add_guard_condition(shutdown_guard_condition_);
88 
89  // we need to initially rebuild the collection,
90  // so that the notify_waitable_ is added
91  collect_entities();
92 }
93 
95 {
96  std::lock_guard<std::mutex> guard(mutex_);
97 
98  notify_waitable_->remove_guard_condition(interrupt_guard_condition_);
99  notify_waitable_->remove_guard_condition(shutdown_guard_condition_);
100  current_collection_.timers.update(
101  {}, {},
102  [this](auto timer) {wait_set_.remove_timer(std::move(timer));});
103 
104  current_collection_.subscriptions.update(
105  {}, {},
106  [this](auto subscription) {
107  wait_set_.remove_subscription(std::move(subscription), kDefaultSubscriptionMask);
108  });
109 
110  current_collection_.clients.update(
111  {}, {},
112  [this](auto client) {wait_set_.remove_client(std::move(client));});
113 
114  current_collection_.services.update(
115  {}, {},
116  [this](auto service) {wait_set_.remove_service(std::move(service));});
117 
118  current_collection_.guard_conditions.update(
119  {}, {},
120  [this](auto guard_condition) {wait_set_.remove_guard_condition(std::move(guard_condition));});
121 
122  current_collection_.waitables.update(
123  {}, {},
124  [this](auto waitable) {wait_set_.remove_waitable(std::move(waitable));});
125 
126  // Remove shutdown callback handle registered to Context
127  if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
128  RCUTILS_LOG_ERROR_NAMED(
129  "rclcpp",
130  "failed to remove registered on_shutdown callback");
131  rcl_reset_error();
132  }
133 }
134 
135 void
137 {
138  this->entities_need_rebuild_.store(true);
139 
140  if (!spinning.load() && entities_need_rebuild_.exchange(false)) {
141  std::lock_guard<std::mutex> guard(mutex_);
142  this->collect_entities();
143  }
144 
145  if (notify) {
146  interrupt_guard_condition_->trigger();
147  }
148 }
149 
150 std::vector<rclcpp::CallbackGroup::WeakPtr>
152 {
154  return this->collector_.get_all_callback_groups();
155 }
156 
157 std::vector<rclcpp::CallbackGroup::WeakPtr>
159 {
162 }
163 
164 std::vector<rclcpp::CallbackGroup::WeakPtr>
166 {
169 }
170 
171 void
173  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
174  [[maybe_unused]] const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
175  bool notify)
176 {
177  this->collector_.add_callback_group(group_ptr);
178 
179  try {
180  this->handle_updated_entities(notify);
181  } catch (const rclcpp::exceptions::RCLError & ex) {
182  throw std::runtime_error(
183  std::string(
184  "Failed to handle entities update on callback group add: ") + ex.what());
185  }
186 }
187 
188 void
190  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
191  bool notify)
192 {
193  if (node_ptr->get_context() != context_) {
194  throw std::runtime_error(
195  "add_node() called with a node with a different context from this executor");
196  }
197 
198  this->collector_.add_node(node_ptr);
199 
200  try {
201  this->handle_updated_entities(notify);
202  } catch (const rclcpp::exceptions::RCLError & ex) {
203  throw std::runtime_error(
204  std::string(
205  "Failed to handle entities update on node add: ") + ex.what());
206  }
207 }
208 
209 void
211  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
212  bool notify)
213 {
214  this->collector_.remove_callback_group(group_ptr);
215 
216  try {
217  this->handle_updated_entities(notify);
218  } catch (const rclcpp::exceptions::RCLError & ex) {
219  throw std::runtime_error(
220  std::string(
221  "Failed to handle entities update on callback group remove: ") + ex.what());
222  }
223 }
224 
225 void
226 Executor::add_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify)
227 {
228  this->add_node(node_ptr->get_node_base_interface(), notify);
229 }
230 
231 void
233  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
234  bool notify)
235 {
236  this->collector_.remove_node(node_ptr);
237 
238  try {
239  this->handle_updated_entities(notify);
240  } catch (const rclcpp::exceptions::RCLError & ex) {
241  throw std::runtime_error(
242  std::string(
243  "Failed to handle entities update on node remove: ") + ex.what());
244  }
245 }
246 
247 void
248 Executor::remove_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify)
249 {
250  this->remove_node(node_ptr->get_node_base_interface(), notify);
251 }
252 
253 void
255  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node,
256  std::chrono::nanoseconds timeout)
257 {
258  this->add_node(node, false);
259  // non-blocking = true
260  spin_once(timeout);
261  this->remove_node(node, false);
262 }
263 
266  std::chrono::nanoseconds timeout,
267  const std::function<std::future_status(std::chrono::nanoseconds wait_time)> & wait_for_future)
268 {
269  // TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
270  // inside a callback executed by an executor.
271 
272  // Check the future before entering the while loop.
273  // If the future is already complete, don't try to spin.
274  std::future_status status = wait_for_future(std::chrono::seconds(0));
275  if (status == std::future_status::ready) {
276  return FutureReturnCode::SUCCESS;
277  }
278 
279  auto end_time = std::chrono::steady_clock::now();
280  std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
281  timeout);
282  if (timeout_ns > std::chrono::nanoseconds::zero()) {
283  end_time += timeout_ns;
284  }
285  std::chrono::nanoseconds timeout_left = timeout_ns;
286 
287  if (spinning.exchange(true)) {
288  throw std::runtime_error("spin_until_future_complete() called while already spinning");
289  }
290  RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
291  while (rclcpp::ok(this->context_) && spinning.load()) {
292  // Do one item of work.
293  spin_once_impl(timeout_left);
294 
295  // Check if the future is set, return SUCCESS if it is.
296  status = wait_for_future(std::chrono::seconds(0));
297  if (status == std::future_status::ready) {
298  return FutureReturnCode::SUCCESS;
299  }
300  // If the original timeout is < 0, then this is blocking, never TIMEOUT.
301  if (timeout_ns < std::chrono::nanoseconds::zero()) {
302  continue;
303  }
304  // Otherwise check if we still have time to wait, return TIMEOUT if not.
305  auto now = std::chrono::steady_clock::now();
306  if (now >= end_time) {
307  return FutureReturnCode::TIMEOUT;
308  }
309  // Subtract the elapsed time from the original timeout.
310  timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
311  }
312 
313  // The future did not complete before ok() returned false, return INTERRUPTED.
314  return FutureReturnCode::INTERRUPTED;
315 }
316 
317 void
318 Executor::spin_node_some(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node)
319 {
320  this->add_node(node, false);
321  spin_some();
322  this->remove_node(node, false);
323 }
324 
325 void
326 Executor::spin_node_some(const std::shared_ptr<rclcpp::Node> & node)
327 {
328  this->spin_node_some(node->get_node_base_interface());
329 }
330 
331 void Executor::spin_some(std::chrono::nanoseconds max_duration)
332 {
333  return this->spin_some_impl(max_duration, false);
334 }
335 
336 void
338  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node,
339  std::chrono::nanoseconds max_duration)
340 {
341  this->add_node(node, false);
342  spin_all(max_duration);
343  this->remove_node(node, false);
344 }
345 
346 void
348  const std::shared_ptr<rclcpp::Node> & node,
349  std::chrono::nanoseconds max_duration)
350 {
351  this->spin_node_all(node->get_node_base_interface(), max_duration);
352 }
353 
354 void Executor::spin_all(std::chrono::nanoseconds max_duration)
355 {
356  if (max_duration < 0ns) {
357  throw std::invalid_argument("max_duration must be greater than or equal to 0");
358  }
359  return this->spin_some_impl(max_duration, true);
360 }
361 
362 void
363 Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
364 {
365  auto start = std::chrono::steady_clock::now();
366  auto max_duration_not_elapsed = [max_duration, start]() {
367  if (std::chrono::nanoseconds(0) == max_duration) {
368  // told to spin forever if need be
369  return true;
370  } else if (std::chrono::steady_clock::now() - start < max_duration) {
371  // told to spin only for some maximum amount of time
372  return true;
373  }
374  // spun too long
375  return false;
376  };
377 
378  if (spinning.exchange(true)) {
379  throw std::runtime_error("spin_some() called while already spinning");
380  }
381  RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
382 
383  // clear the wait result and wait for work without blocking to collect the work
384  // for the first time
385  // both spin_some and spin_all wait for work at the beginning
386  wait_result_.reset();
387  wait_for_work(std::chrono::milliseconds(0));
388  bool entity_states_fully_polled = true;
389 
390  if (entities_need_rebuild_) {
391  // if the last wait triggered a collection rebuild, we need to call
392  // wait_for_work once more, in order to do a collection rebuild and collect
393  // events from the just added entities
394  entity_states_fully_polled = false;
395  }
396 
397  // The logic of this while loop is as follows:
398  //
399  // - while not shutdown, and spinning (not canceled), and not max duration reached...
400  // - try to get an executable item to execute, and execute it if available
401  // - otherwise, reset the wait result, and ...
402  // - if there was no work available just after waiting, break the loop unconditionally
403  // - this is appropriate for both spin_some and spin_all which use this function
404  // - else if exhaustive = true, then wait for work again
405  // - this is only used for spin_all and not spin_some
406  // - else break
407  // - this only occurs with spin_some
408  //
409  // The logic of this loop is subtle and should be carefully changed if at all.
410  // See also:
411  // https://github.com/ros2/rclcpp/issues/2508
412  // https://github.com/ros2/rclcpp/pull/2517
413  while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
414  AnyExecutable any_exec;
415  if (get_next_ready_executable(any_exec)) {
416  execute_any_executable(any_exec);
417  // during the execution some entity might got ready therefore we need
418  // to repoll the states of all entities
419  entity_states_fully_polled = false;
420  } else {
421  // if nothing is ready, reset the result to clear it
422  wait_result_.reset();
423 
424  if (entity_states_fully_polled) {
425  // there was no work after just waiting, always exit in this case
426  // before the exhaustive condition can be checked
427  break;
428  }
429 
430  if (exhaustive) {
431  // if exhaustive, wait for work again
432  // this only happens for spin_all; spin_some only waits at the start
433  wait_for_work(std::chrono::milliseconds(0));
434  entity_states_fully_polled = true;
435  if (entities_need_rebuild_) {
436  // if the last wait triggered a collection rebuild, we need to call
437  // wait_for_work once more, in order to do a collection rebuild and
438  // collect events from the just added entities
439  entity_states_fully_polled = false;
440  }
441  } else {
442  break;
443  }
444  }
445  }
446 }
447 
448 void
449 Executor::spin_once_impl(std::chrono::nanoseconds timeout)
450 {
451  AnyExecutable any_exec;
452  if (get_next_executable(any_exec, timeout)) {
453  execute_any_executable(any_exec);
454  }
455 }
456 
457 void
458 Executor::spin_once(std::chrono::nanoseconds timeout)
459 {
460  if (spinning.exchange(true)) {
461  throw std::runtime_error("spin_once() called while already spinning");
462  }
463  RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
464  spin_once_impl(timeout);
465 }
466 
467 void
469 {
470  spinning.store(false);
471  try {
472  interrupt_guard_condition_->trigger();
473  } catch (const rclcpp::exceptions::RCLError & ex) {
474  throw std::runtime_error(
475  std::string("Failed to trigger guard condition in cancel: ") + ex.what());
476  }
477 }
478 
479 void
481 {
482  if (!spinning.load()) {
483  return;
484  }
485 
486  assert(
487  (void("cannot execute an AnyExecutable without a valid callback group"),
488  any_exec.callback_group));
489 
490  if (any_exec.timer) {
491  TRACETOOLS_TRACEPOINT(
492  rclcpp_executor_execute,
493  static_cast<const void *>(any_exec.timer->get_timer_handle().get()));
494  execute_timer(any_exec.timer, any_exec.data);
495  }
496  if (any_exec.subscription) {
497  TRACETOOLS_TRACEPOINT(
498  rclcpp_executor_execute,
499  static_cast<const void *>(any_exec.subscription->get_subscription_handle().get()));
500  execute_subscription(any_exec.subscription);
501  }
502  if (any_exec.service) {
503  execute_service(any_exec.service);
504  }
505  if (any_exec.client) {
506  execute_client(any_exec.client);
507  }
508  if (any_exec.waitable) {
509  const std::shared_ptr<void> & const_data = any_exec.data;
510  any_exec.waitable->execute(const_data);
511  }
512 
513  // Reset the callback_group, regardless of type
514  any_exec.callback_group->can_be_taken_from().store(true);
515 }
516 
517 template<typename Taker, typename Handler>
518 static
519 void
520 take_and_do_error_handling(
521  const char * action_description,
522  const char * topic_or_service_name,
523  Taker take_action,
524  Handler handle_action)
525 {
526  bool taken = false;
527  try {
528  taken = take_action();
529  } catch (const rclcpp::exceptions::RCLError & rcl_error) {
530  RCLCPP_ERROR(
531  rclcpp::get_logger("rclcpp"),
532  "executor %s '%s' unexpectedly failed: %s",
533  action_description,
534  topic_or_service_name,
535  rcl_error.what());
536  }
537  if (taken) {
538  handle_action();
539  } else {
540  // Message or Service was not taken for some reason.
541  // Note that this can be normal, if the underlying middleware needs to
542  // interrupt wait spuriously it is allowed.
543  // So in that case the executor cannot tell the difference in a
544  // spurious wake up and an entity actually having data until trying
545  // to take the data.
546  RCLCPP_DEBUG(
547  rclcpp::get_logger("rclcpp"),
548  "executor %s '%s' failed to take anything",
549  action_description,
550  topic_or_service_name);
551  }
552 }
553 
554 void
555 Executor::execute_subscription(const rclcpp::SubscriptionBase::SharedPtr & subscription)
556 {
558 
559  rclcpp::MessageInfo message_info;
560  message_info.get_rmw_message_info().from_intra_process = false;
561 
562  switch (subscription->get_delivered_message_kind()) {
563  // Deliver ROS message
564  case rclcpp::DeliveredMessageKind::ROS_MESSAGE:
565  {
566  if (subscription->can_loan_messages()) {
567  // This is the case where a loaned message is taken from the middleware via
568  // inter-process communication, given to the user for their callback,
569  // and then returned.
570  void * loaned_msg = nullptr;
571  // TODO(wjwwood): refactor this into methods on subscription when LoanedMessage
572  // is extened to support subscriptions as well.
573  take_and_do_error_handling(
574  "taking a loaned message from topic",
575  subscription->get_topic_name(),
576  [&]()
577  {
578  rcl_ret_t ret = rcl_take_loaned_message(
579  subscription->get_subscription_handle().get(),
580  &loaned_msg,
581  &message_info.get_rmw_message_info(),
582  nullptr);
583  TRACETOOLS_TRACEPOINT(rclcpp_take, static_cast<const void *>(loaned_msg));
584  if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
585  return false;
586  } else if (RCL_RET_OK != ret) {
587  rclcpp::exceptions::throw_from_rcl_error(ret);
588  }
589  return true;
590  },
591  [&]() {subscription->handle_loaned_message(loaned_msg, message_info);});
592  if (nullptr != loaned_msg) {
594  subscription->get_subscription_handle().get(), loaned_msg);
595  if (RCL_RET_OK != ret) {
596  RCLCPP_ERROR(
597  rclcpp::get_logger("rclcpp"),
598  "rcl_return_loaned_message_from_subscription() failed for subscription on topic "
599  "'%s': %s",
600  subscription->get_topic_name(), rcl_get_error_string().str);
601  rcl_reset_error();
602  }
603  loaned_msg = nullptr;
604  }
605  } else {
606  // This case is taking a copy of the message data from the middleware via
607  // inter-process communication.
608  std::shared_ptr<void> message = subscription->create_message();
609  take_and_do_error_handling(
610  "taking a message from topic",
611  subscription->get_topic_name(),
612  [&]() {return subscription->take_type_erased(message.get(), message_info);},
613  [&]() {subscription->handle_message(message, message_info);});
614  // TODO(clalancette): In the case that the user is using the MessageMemoryPool,
615  // and they take a shared_ptr reference to the message in the callback, this can
616  // inadvertently return the message to the pool when the user is still using it.
617  // This is a bug that needs to be fixed in the pool, and we should probably have
618  // a custom deleter for the message that actually does the return_message().
619  subscription->return_message(message);
620  }
621  break;
622  }
623 
624  // Deliver serialized message
625  case rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE:
626  {
627  // This is the case where a copy of the serialized message is taken from
628  // the middleware via inter-process communication.
629  std::shared_ptr<SerializedMessage> serialized_msg =
630  subscription->create_serialized_message();
631  take_and_do_error_handling(
632  "taking a serialized message from topic",
633  subscription->get_topic_name(),
634  [&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
635  [&]()
636  {
637  subscription->handle_serialized_message(serialized_msg, message_info);
638  });
639  subscription->return_serialized_message(serialized_msg);
640  break;
641  }
642 
643  // DYNAMIC SUBSCRIPTION ========================================================================
644  // Deliver dynamic message
645  case rclcpp::DeliveredMessageKind::DYNAMIC_MESSAGE:
646  {
647  throw std::runtime_error("Unimplemented");
648  }
649 
650  case rclcpp::DeliveredMessageKind::INVALID:
651  {
652  throw std::runtime_error("Delivered message kind is not supported");
653  }
654  }
655 }
656 
657 void
658 Executor::execute_timer(
659  const rclcpp::TimerBase::SharedPtr & timer,
660  const std::shared_ptr<void> & data_ptr)
661 {
662  timer->execute_callback(data_ptr);
663 }
664 
665 void
666 Executor::execute_service(const rclcpp::ServiceBase::SharedPtr & service)
667 {
668  auto request_header = service->create_request_header();
669  std::shared_ptr<void> request = service->create_request();
670  take_and_do_error_handling(
671  "taking a service server request from service",
672  service->get_service_name(),
673  [&]() {return service->take_type_erased_request(request.get(), *request_header);},
674  [&]() {service->handle_request(request_header, request);});
675 }
676 
677 void
678 Executor::execute_client(const rclcpp::ClientBase::SharedPtr & client)
679 {
680  auto request_header = client->create_request_header();
681  std::shared_ptr<void> response = client->create_response();
682  take_and_do_error_handling(
683  "taking a service client response from service",
684  client->get_service_name(),
685  [&]() {return client->take_type_erased_response(response.get(), *request_header);},
686  [&]() {client->handle_response(request_header, response);});
687 }
688 
689 void
691 {
692  // Updating the entity collection and waitset expires any active result
693  this->wait_result_.reset();
694 
695  // Get the current list of available waitables from the collector.
698  auto callback_groups = this->collector_.get_all_callback_groups();
699  rclcpp::executors::build_entities_collection(callback_groups, collection);
700 
701  // Make a copy of notify waitable so we can continue to mutate the original
702  // one outside of the execute loop.
703  // This prevents the collection of guard conditions in the waitable from changing
704  // while we are waiting on it.
705  if (notify_waitable_) {
706  current_notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
708  auto notify_waitable = std::static_pointer_cast<rclcpp::Waitable>(current_notify_waitable_);
709  collection.waitables.insert({notify_waitable.get(), {notify_waitable, {}}});
710  }
711 
712  // We must remove expired entities here, so that we don't continue to use older entities.
713  // See https://github.com/ros2/rclcpp/issues/2180 for more information.
714  current_collection_.remove_expired_entities();
715 
716  // Update each of the groups of entities in the current collection, adding or removing
717  // from the wait set as necessary.
718  current_collection_.timers.update(
719  collection.timers,
720  [this](auto timer) {wait_set_.add_timer(std::move(timer));},
721  [this](auto timer) {wait_set_.remove_timer(std::move(timer));});
722 
723  current_collection_.subscriptions.update(
724  collection.subscriptions,
725  [this](auto subscription) {
726  wait_set_.add_subscription(std::move(subscription), kDefaultSubscriptionMask);
727  },
728  [this](auto subscription) {
729  wait_set_.remove_subscription(std::move(subscription), kDefaultSubscriptionMask);
730  });
731 
732  current_collection_.clients.update(
733  collection.clients,
734  [this](auto client) {wait_set_.add_client(std::move(client));},
735  [this](auto client) {wait_set_.remove_client(std::move(client));});
736 
737  current_collection_.services.update(
738  collection.services,
739  [this](auto service) {wait_set_.add_service(std::move(service));},
740  [this](auto service) {wait_set_.remove_service(std::move(service));});
741 
742  current_collection_.guard_conditions.update(
743  collection.guard_conditions,
744  [this](auto guard_condition) {wait_set_.add_guard_condition(std::move(guard_condition));},
745  [this](auto guard_condition) {wait_set_.remove_guard_condition(std::move(guard_condition));});
746 
747  current_collection_.waitables.update(
748  collection.waitables,
749  [this](auto waitable) {wait_set_.add_waitable(std::move(waitable));},
750  [this](auto waitable) {wait_set_.remove_waitable(std::move(waitable));});
751 
752  // In the case that an entity already has an expired weak pointer
753  // before being removed from the waitset, additionally prune the waitset.
754  this->wait_set_.prune_deleted_entities();
755 }
756 
757 void
758 Executor::wait_for_work(std::chrono::nanoseconds timeout)
759 {
760  TRACETOOLS_TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
761 
762  // Clear any previous wait result
763  this->wait_result_.reset();
764 
765  {
766  std::lock_guard<std::mutex> guard(mutex_);
767 
768  if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
769  this->collect_entities();
770  }
771  }
772 
773  this->wait_result_.emplace(wait_set_.wait(timeout));
774 
775  if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
776  RCUTILS_LOG_WARN_NAMED(
777  "rclcpp",
778  "empty wait set received in wait(). This should never happen.");
779  } else {
780  if (this->wait_result_->kind() == WaitResultKind::Ready && current_notify_waitable_) {
781  auto & rcl_wait_set = this->wait_result_->get_wait_set().get_rcl_wait_set();
782  if (current_notify_waitable_->is_ready(rcl_wait_set)) {
783  current_notify_waitable_->execute(current_notify_waitable_->take_data());
784  }
785  }
786  }
787 }
788 
789 bool
791 {
792  TRACETOOLS_TRACEPOINT(rclcpp_executor_get_next_ready);
793 
794  bool valid_executable = false;
795 
796  if (!wait_result_.has_value() || wait_result_->kind() != rclcpp::WaitResultKind::Ready) {
797  return false;
798  }
799 
800  if (!valid_executable) {
801  size_t current_timer_index = 0;
802  while (true) {
803  auto [timer, timer_index] = wait_result_->peek_next_ready_timer(current_timer_index);
804  if (nullptr == timer) {
805  break;
806  }
807  current_timer_index = timer_index;
808  auto entity_iter = current_collection_.timers.find(timer->get_timer_handle().get());
809  if (entity_iter != current_collection_.timers.end()) {
810  auto callback_group = entity_iter->second.callback_group.lock();
811  if (!callback_group || !callback_group->can_be_taken_from()) {
812  current_timer_index++;
813  continue;
814  }
815  // At this point the timer is either ready for execution or was perhaps
816  // it was canceled, based on the result of call(), but either way it
817  // should not be checked again from peek_next_ready_timer(), so clear
818  // it from the wait result.
819  wait_result_->clear_timer_with_index(current_timer_index);
820  // Check that the timer should be called still, i.e. it wasn't canceled.
821  any_executable.data = timer->call();
822  if (!any_executable.data) {
823  current_timer_index++;
824  continue;
825  }
826  any_executable.timer = timer;
827  any_executable.callback_group = callback_group;
828  valid_executable = true;
829  break;
830  }
831  current_timer_index++;
832  }
833  }
834 
835  if (!valid_executable) {
836  while (auto subscription = wait_result_->next_ready_subscription()) {
837  auto entity_iter = current_collection_.subscriptions.find(
838  subscription->get_subscription_handle().get());
839  if (entity_iter != current_collection_.subscriptions.end()) {
840  auto callback_group = entity_iter->second.callback_group.lock();
841  if (!callback_group || !callback_group->can_be_taken_from()) {
842  continue;
843  }
844  any_executable.subscription = subscription;
845  any_executable.callback_group = callback_group;
846  valid_executable = true;
847  break;
848  }
849  }
850  }
851 
852  if (!valid_executable) {
853  while (auto service = wait_result_->next_ready_service()) {
854  auto entity_iter = current_collection_.services.find(service->get_service_handle().get());
855  if (entity_iter != current_collection_.services.end()) {
856  auto callback_group = entity_iter->second.callback_group.lock();
857  if (!callback_group || !callback_group->can_be_taken_from()) {
858  continue;
859  }
860  any_executable.service = service;
861  any_executable.callback_group = callback_group;
862  valid_executable = true;
863  break;
864  }
865  }
866  }
867 
868  if (!valid_executable) {
869  while (auto client = wait_result_->next_ready_client()) {
870  auto entity_iter = current_collection_.clients.find(client->get_client_handle().get());
871  if (entity_iter != current_collection_.clients.end()) {
872  auto callback_group = entity_iter->second.callback_group.lock();
873  if (!callback_group || !callback_group->can_be_taken_from()) {
874  continue;
875  }
876  any_executable.client = client;
877  any_executable.callback_group = callback_group;
878  valid_executable = true;
879  break;
880  }
881  }
882  }
883 
884  if (!valid_executable) {
885  while (auto waitable = wait_result_->next_ready_waitable()) {
886  auto entity_iter = current_collection_.waitables.find(waitable.get());
887  if (entity_iter != current_collection_.waitables.end()) {
888  auto callback_group = entity_iter->second.callback_group.lock();
889  if (!callback_group || !callback_group->can_be_taken_from()) {
890  continue;
891  }
892  any_executable.waitable = waitable;
893  any_executable.callback_group = callback_group;
894  any_executable.data = waitable->take_data();
895  valid_executable = true;
896  break;
897  }
898  }
899  }
900 
901  if (any_executable.callback_group) {
902  if (any_executable.callback_group->type() == CallbackGroupType::MutuallyExclusive) {
903  assert(any_executable.callback_group->can_be_taken_from().load());
904  any_executable.callback_group->can_be_taken_from().store(false);
905  }
906  }
907 
908 
909  return valid_executable;
910 }
911 
912 bool
913 Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanoseconds timeout)
914 {
915  bool success = false;
916  // Check to see if there are any subscriptions or timers needing service
917  // TODO(wjwwood): improve run to run efficiency of this function
918  success = get_next_ready_executable(any_executable);
919  // If there are none
920  if (!success) {
921  // Wait for subscriptions or timers to work on
922  wait_for_work(timeout);
923  if (!spinning.load()) {
924  return false;
925  }
926  // Try again
927  success = get_next_ready_executable(any_executable);
928  }
929  return success;
930 }
931 
932 bool
934 {
935  return spinning;
936 }
Coordinate the order and timing of available communication tasks.
Definition: executor.hpp:65
std::shared_ptr< rclcpp::GuardCondition > interrupt_guard_condition_
Guard condition for signaling the rmw layer to wake up for special events.
Definition: executor.hpp:571
virtual RCLCPP_PUBLIC void spin_node_some(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node)
Add a node, complete all immediately available work, and remove the node.
Definition: executor.cpp:318
virtual RCLCPP_PUBLIC void spin_node_all(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node, std::chrono::nanoseconds max_duration)
Add a node, complete all immediately available work exhaustively, and remove the node.
Definition: executor.cpp:337
virtual RCLCPP_PUBLIC ~Executor()
Default destructor.
Definition: executor.cpp:94
virtual RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_manually_added_callback_groups()
Get callback groups that belong to executor.
Definition: executor.cpp:158
RCLCPP_PUBLIC void wait_for_work(std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Block until more work becomes avilable or timeout is reached.
Definition: executor.cpp:758
RCLCPP_PUBLIC void spin_node_once_nanoseconds(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node, std::chrono::nanoseconds timeout)
Add a node to executor, execute the next available unit of work, and remove the node.
Definition: executor.cpp:254
RCLCPP_PUBLIC Executor(const rclcpp::ExecutorOptions &options=rclcpp::ExecutorOptions())
Default constructor.
Definition: executor.cpp:63
RCLCPP_PUBLIC bool get_next_executable(AnyExecutable &any_executable, std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Wait for executable in ready state and populate union structure.
Definition: executor.cpp:913
virtual RCLCPP_PUBLIC void spin_once(std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
Collect work once and execute the next available work, optionally within a duration.
Definition: executor.cpp:458
virtual RCLCPP_PUBLIC void spin_some(std::chrono::nanoseconds max_duration=std::chrono::nanoseconds(0))
Collect work once and execute all available work, optionally within a max duration.
Definition: executor.cpp:331
virtual RCLCPP_PUBLIC void cancel()
Cancel any running spin* function, causing it to return.
Definition: executor.cpp:468
static RCLCPP_PUBLIC void execute_timer(const rclcpp::TimerBase::SharedPtr &timer, const std::shared_ptr< void > &data_ptr)
Run timer executable.
Definition: executor.cpp:658
RCLCPP_PUBLIC bool is_spinning()
Returns true if the executor is currently spinning.
Definition: executor.cpp:933
virtual RCLCPP_PUBLIC void handle_updated_entities(bool notify)
This function triggers a recollect of all entities that are registered to the executor.
Definition: executor.cpp:136
RCLCPP_PUBLIC bool get_next_ready_executable(AnyExecutable &any_executable)
Check for executable in ready state and populate union structure.
Definition: executor.cpp:790
std::shared_ptr< rclcpp::Context > context_
The context associated with this executor.
Definition: executor.hpp:579
virtual RCLCPP_PUBLIC void remove_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true)
Remove a node from the executor.
Definition: executor.cpp:232
RCLCPP_PUBLIC void collect_entities()
Gather all of the waitable entities from associated nodes and callback groups.
Definition: executor.cpp:690
virtual RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_automatically_added_callback_groups_from_nodes()
Get callback groups that belong to executor.
Definition: executor.cpp:165
virtual RCLCPP_PUBLIC FutureReturnCode spin_until_future_complete_impl(std::chrono::nanoseconds timeout, const std::function< std::future_status(std::chrono::nanoseconds wait_time)> &wait_for_future)
Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
Definition: executor.cpp:265
virtual RCLCPP_PUBLIC void remove_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr, bool notify=true)
Remove a callback group from the executor.
Definition: executor.cpp:210
static RCLCPP_PUBLIC void execute_client(const rclcpp::ClientBase::SharedPtr &client)
Run service client executable.
Definition: executor.cpp:678
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_
shutdown callback handle registered to Context
Definition: executor.hpp:614
static RCLCPP_PUBLIC void execute_service(const rclcpp::ServiceBase::SharedPtr &service)
Run service server executable.
Definition: executor.cpp:666
RCLCPP_PUBLIC void execute_any_executable(AnyExecutable &any_exec)
Find the next available executable and do the work associated with it.
Definition: executor.cpp:480
std::shared_ptr< rclcpp::executors::ExecutorNotifyWaitable > notify_waitable_
Waitable containing guard conditions controlling the executor flow.
Definition: executor.hpp:594
virtual RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_all_callback_groups()
Get callback groups that belong to executor.
Definition: executor.cpp:151
virtual RCLCPP_PUBLIC void add_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr, const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true)
Add a callback group to an executor.
Definition: executor.cpp:172
virtual RCLCPP_PUBLIC void add_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true)
Add a node to the executor.
Definition: executor.cpp:189
rclcpp::executors::ExecutorEntitiesCollector collector_
Collector used to associate executable entities from nodes and guard conditions.
Definition: executor.hpp:599
virtual RCLCPP_PUBLIC void spin_all(std::chrono::nanoseconds max_duration)
Collect and execute work repeatedly within a duration or until no more work is available.
Definition: executor.cpp:354
RCLCPP_PUBLIC void spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
Collect work and execute available work, optionally within a duration.
Definition: executor.cpp:363
static RCLCPP_PUBLIC void execute_subscription(const rclcpp::SubscriptionBase::SharedPtr &subscription)
Run subscription executable.
Definition: executor.cpp:555
std::shared_ptr< rclcpp::GuardCondition > shutdown_guard_condition_
Guard condition for signaling the rmw layer to wake up for system shutdown.
Definition: executor.hpp:574
std::atomic_bool spinning
Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
Definition: executor.hpp:568
A condition that can be waited on in a single wait set and asynchronously triggered.
Additional meta data about messages taken from subscriptions.
const rmw_message_info_t & get_rmw_message_info() const
Return the message info as the underlying rmw message info type.
Options used to determine what parts of a subscription get added to or removed from a wait set.
Created when the return code does not match one of the other specialized exceptions.
Definition: exceptions.hpp:162
void update(const EntityCollection< EntityKeyType, EntityValueType > &other, std::function< void(const EntitySharedPtr &)> on_added, std::function< void(const EntitySharedPtr &)> on_removed)
Update this collection based on the contents of another collection.
RCLCPP_PUBLIC void add_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr)
Add a node to the entity collector.
RCLCPP_PUBLIC void update_collections()
Update the underlying collections.
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_all_callback_groups() const
Get all callback groups known to this entity collector.
RCLCPP_PUBLIC void add_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr)
Add a callback group to the entity collector.
RCLCPP_PUBLIC void remove_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr)
Remove a callback group from the entity collector.
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_automatically_added_callback_groups() const
Get automatically-added callback groups known to this entity collector.
RCLCPP_PUBLIC void remove_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr)
Remove a node from the entity collector.
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_manually_added_callback_groups() const
Get manually-added callback groups known to this entity collector.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC bool ok(const rclcpp::Context::SharedPtr &context=rclcpp::contexts::get_global_default_context())
Check rclcpp's status.
FutureReturnCode
Return codes to be used with spin_until_future_complete.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
Options to be passed to the executor constructor.
Represent the total set of entities for a single executor.
TimerCollection timers
Collection of timers currently in use by the executor.
GuardConditionCollection guard_conditions
Collection of guard conditions currently in use by the executor.
ServiceCollection services
Collection of services currently in use by the executor.
SubscriptionCollection subscriptions
Collection of subscriptions currently in use by the executor.
WaitableCollection waitables
Collection of waitables currently in use by the executor.
ClientCollection clients
Collection of clients currently in use by the executor.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_return_loaned_message_from_subscription(const rcl_subscription_t *subscription, void *loaned_message)
Return a loaned message from a topic using a rcl subscription.
Definition: subscription.c:764
#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