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