ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
wait_set_template.hpp
1 // Copyright 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 #ifndef RCLCPP__WAIT_SET_TEMPLATE_HPP_
16 #define RCLCPP__WAIT_SET_TEMPLATE_HPP_
17 
18 #include <chrono>
19 #include <memory>
20 #include <utility>
21 
22 #include "rcl/wait.h"
23 #include "rcpputils/scope_exit.hpp"
24 
25 #include "rclcpp/client.hpp"
26 #include "rclcpp/context.hpp"
27 #include "rclcpp/contexts/default_context.hpp"
28 #include "rclcpp/guard_condition.hpp"
29 #include "rclcpp/macros.hpp"
30 #include "rclcpp/service.hpp"
31 #include "rclcpp/subscription_base.hpp"
32 #include "rclcpp/subscription_wait_set_mask.hpp"
33 #include "rclcpp/timer.hpp"
34 #include "rclcpp/visibility_control.hpp"
35 #include "rclcpp/wait_result.hpp"
36 #include "rclcpp/waitable.hpp"
37 
38 namespace rclcpp
39 {
40 
42 
46 template<class SynchronizationPolicy, class StoragePolicy>
47 class WaitSetTemplate final : private SynchronizationPolicy, private StoragePolicy
48 {
49 public:
50  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(WaitSetTemplate)
51 
52  using typename StoragePolicy::SubscriptionEntry;
53  using typename StoragePolicy::WaitableEntry;
54 
56 
70  explicit
72  const typename StoragePolicy::SubscriptionsIterable & subscriptions = {},
73  const typename StoragePolicy::GuardConditionsIterable & guard_conditions = {},
74  const typename StoragePolicy::TimersIterable & timers = {},
75  const typename StoragePolicy::ClientsIterable & clients = {},
76  const typename StoragePolicy::ServicesIterable & services = {},
77  const typename StoragePolicy::WaitablesIterable & waitables = {},
78  rclcpp::Context::SharedPtr context =
79  rclcpp::contexts::get_global_default_context())
80  : SynchronizationPolicy(context),
81  StoragePolicy(
82  subscriptions,
83  guard_conditions,
84  // this method comes from the SynchronizationPolicy
85  this->get_extra_guard_conditions(),
86  timers,
87  clients,
88  services,
89  waitables,
90  context)
91  {}
92 
94 
99  const rcl_wait_set_t &
101  {
102  // this method comes from the StoragePolicy
103  return this->storage_get_rcl_wait_set();
104  }
105 
107 
129  void
131  std::shared_ptr<rclcpp::SubscriptionBase> subscription,
133  {
134  if (nullptr == subscription) {
135  throw std::invalid_argument("subscription is nullptr");
136  }
137  // this method comes from the SynchronizationPolicy
138  this->sync_add_subscription(
139  std::move(subscription),
140  mask,
141  [this](
142  std::shared_ptr<rclcpp::SubscriptionBase> && inner_subscription,
143  const rclcpp::SubscriptionWaitSetMask & mask)
144  {
145  // These methods comes from the StoragePolicy, and may not exist for
146  // fixed sized storage policies.
147  // It will throw if the subscription has already been added.
148  if (mask.include_subscription) {
149  auto local_subscription = inner_subscription;
150  bool already_in_use =
151  local_subscription->exchange_in_use_by_wait_set_state(local_subscription.get(), true);
152  if (already_in_use) {
153  throw std::runtime_error("subscription already associated with a wait set");
154  }
155  this->storage_add_subscription(std::move(local_subscription));
156  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
157  }
158  if (mask.include_events) {
159  for (auto key_event_pair : inner_subscription->get_event_handlers()) {
160  auto event = key_event_pair.second;
161  auto local_subscription = inner_subscription;
162  bool already_in_use =
163  local_subscription->exchange_in_use_by_wait_set_state(event.get(), true);
164  if (already_in_use) {
165  throw std::runtime_error("subscription event already associated with a wait set");
166  }
167  this->storage_add_waitable(std::move(event), std::move(local_subscription));
168  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
169  }
170  }
172  auto local_subscription = inner_subscription;
173  auto waitable = inner_subscription->get_intra_process_waitable();
174  if (nullptr != waitable) {
175  bool already_in_use = local_subscription->exchange_in_use_by_wait_set_state(
176  waitable.get(),
177  true);
178  if (already_in_use) {
179  throw std::runtime_error(
180  "subscription intra-process waitable already associated with a wait set");
181  }
182  this->storage_add_waitable(
183  std::move(inner_subscription->get_intra_process_waitable()),
184  std::move(local_subscription));
185  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
186  }
187  }
188  });
189  }
190 
192 
207  void
209  std::shared_ptr<rclcpp::SubscriptionBase> subscription,
211  {
212  if (nullptr == subscription) {
213  throw std::invalid_argument("subscription is nullptr");
214  }
215  // this method comes from the SynchronizationPolicy
216  this->sync_remove_subscription(
217  std::move(subscription),
218  mask,
219  [this](
220  std::shared_ptr<rclcpp::SubscriptionBase> && inner_subscription,
221  const rclcpp::SubscriptionWaitSetMask & mask)
222  {
223  // This method comes from the StoragePolicy, and it may not exist for
224  // fixed sized storage policies.
225  // It will throw if the subscription is not in the wait set.
226  if (mask.include_subscription) {
227  auto local_subscription = inner_subscription;
228  this->storage_remove_subscription(std::move(local_subscription));
229  inner_subscription->exchange_in_use_by_wait_set_state(inner_subscription.get(), false);
230  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
231  }
232  if (mask.include_events) {
233  for (auto key_event_pair : inner_subscription->get_event_handlers()) {
234  auto event = key_event_pair.second;
235  auto local_subscription = inner_subscription;
236  this->storage_remove_waitable(std::move(event));
237  local_subscription->exchange_in_use_by_wait_set_state(
238  key_event_pair.second.get(), false);
239  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
240  }
241  }
243  auto local_waitable = inner_subscription->get_intra_process_waitable();
244  if (nullptr != local_waitable) {
245  // This is the case when intra process is enabled for the subscription.
246  auto waitable = local_waitable;
247  this->storage_remove_waitable(std::move(local_waitable));
248  inner_subscription->exchange_in_use_by_wait_set_state(waitable.get(), false);
249  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
250  }
251  }
252  });
253  }
254 
256 
282  void
283  add_guard_condition(std::shared_ptr<rclcpp::GuardCondition> guard_condition)
284  {
285  if (nullptr == guard_condition) {
286  throw std::invalid_argument("guard_condition is nullptr");
287  }
288  // this method comes from the SynchronizationPolicy
289  this->sync_add_guard_condition(
290  std::move(guard_condition),
291  [this](std::shared_ptr<rclcpp::GuardCondition> && inner_guard_condition) {
292  bool already_in_use = inner_guard_condition->exchange_in_use_by_wait_set_state(true);
293  if (already_in_use) {
294  throw std::runtime_error("guard condition already in use by another wait set");
295  }
296  // This method comes from the StoragePolicy, and it may not exist for
297  // fixed sized storage policies.
298  // It will throw if the guard condition has already been added.
299  this->storage_add_guard_condition(std::move(inner_guard_condition));
300  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
301  });
302  }
303 
305 
323  void
324  remove_guard_condition(std::shared_ptr<rclcpp::GuardCondition> guard_condition)
325  {
326  if (nullptr == guard_condition) {
327  throw std::invalid_argument("guard_condition is nullptr");
328  }
329  // this method comes from the SynchronizationPolicy
330  this->sync_remove_guard_condition(
331  std::move(guard_condition),
332  [this](std::shared_ptr<rclcpp::GuardCondition> && inner_guard_condition) {
333  // This method comes from the StoragePolicy, and it may not exist for
334  // fixed sized storage policies.
335  // It will throw if the guard condition is not in the wait set.
336  auto local_guard_condition = inner_guard_condition;
337  this->storage_remove_guard_condition(std::move(local_guard_condition));
338  inner_guard_condition->exchange_in_use_by_wait_set_state(false);
339  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
340  });
341  }
342 
344 
353  void
354  add_timer(std::shared_ptr<rclcpp::TimerBase> timer)
355  {
356  if (nullptr == timer) {
357  throw std::invalid_argument("timer is nullptr");
358  }
359  // this method comes from the SynchronizationPolicy
360  this->sync_add_timer(
361  std::move(timer),
362  [this](std::shared_ptr<rclcpp::TimerBase> && inner_timer) {
363  bool already_in_use = inner_timer->exchange_in_use_by_wait_set_state(true);
364  if (already_in_use) {
365  throw std::runtime_error("timer already in use by another wait set");
366  }
367  // This method comes from the StoragePolicy, and it may not exist for
368  // fixed sized storage policies.
369  // It will throw if the timer has already been added.
370  this->storage_add_timer(std::move(inner_timer));
371  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
372  });
373  }
374 
376 
384  void
385  remove_timer(std::shared_ptr<rclcpp::TimerBase> timer)
386  {
387  if (nullptr == timer) {
388  throw std::invalid_argument("timer is nullptr");
389  }
390  // this method comes from the SynchronizationPolicy
391  this->sync_remove_timer(
392  std::move(timer),
393  [this](std::shared_ptr<rclcpp::TimerBase> && inner_timer) {
394  // This method comes from the StoragePolicy, and it may not exist for
395  // fixed sized storage policies.
396  // It will throw if the timer is not in the wait set.
397  auto local_timer = inner_timer;
398  this->storage_remove_timer(std::move(local_timer));
399  inner_timer->exchange_in_use_by_wait_set_state(false);
400  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
401  });
402  }
403 
405 
414  void
415  add_client(std::shared_ptr<rclcpp::ClientBase> client)
416  {
417  if (nullptr == client) {
418  throw std::invalid_argument("client is nullptr");
419  }
420  // this method comes from the SynchronizationPolicy
421  this->sync_add_client(
422  std::move(client),
423  [this](std::shared_ptr<rclcpp::ClientBase> && inner_client) {
424  bool already_in_use = inner_client->exchange_in_use_by_wait_set_state(true);
425  if (already_in_use) {
426  throw std::runtime_error("client already in use by another wait set");
427  }
428  // This method comes from the StoragePolicy, and it may not exist for
429  // fixed sized storage policies.
430  // It will throw if the client has already been added.
431  this->storage_add_client(std::move(inner_client));
432  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
433  });
434  }
435 
437 
445  void
446  remove_client(std::shared_ptr<rclcpp::ClientBase> client)
447  {
448  if (nullptr == client) {
449  throw std::invalid_argument("client is nullptr");
450  }
451  // this method comes from the SynchronizationPolicy
452  this->sync_remove_client(
453  std::move(client),
454  [this](std::shared_ptr<rclcpp::ClientBase> && inner_client) {
455  // This method comes from the StoragePolicy, and it may not exist for
456  // fixed sized storage policies.
457  // It will throw if the client is not in the wait set.
458  auto local_client = inner_client;
459  this->storage_remove_client(std::move(local_client));
460  inner_client->exchange_in_use_by_wait_set_state(false);
461  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
462  });
463  }
464 
466 
475  void
476  add_service(std::shared_ptr<rclcpp::ServiceBase> service)
477  {
478  if (nullptr == service) {
479  throw std::invalid_argument("service is nullptr");
480  }
481  // this method comes from the SynchronizationPolicy
482  this->sync_add_service(
483  std::move(service),
484  [this](std::shared_ptr<rclcpp::ServiceBase> && inner_service) {
485  bool already_in_use = inner_service->exchange_in_use_by_wait_set_state(true);
486  if (already_in_use) {
487  throw std::runtime_error("service already in use by another wait set");
488  }
489  // This method comes from the StoragePolicy, and it may not exist for
490  // fixed sized storage policies.
491  // It will throw if the service has already been added.
492  this->storage_add_service(std::move(inner_service));
493  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
494  });
495  }
496 
498 
506  void
507  remove_service(std::shared_ptr<rclcpp::ServiceBase> service)
508  {
509  if (nullptr == service) {
510  throw std::invalid_argument("service is nullptr");
511  }
512  // this method comes from the SynchronizationPolicy
513  this->sync_remove_service(
514  std::move(service),
515  [this](std::shared_ptr<rclcpp::ServiceBase> && inner_service) {
516  // This method comes from the StoragePolicy, and it may not exist for
517  // fixed sized storage policies.
518  // It will throw if the service is not in the wait set.
519  auto local_service = inner_service;
520  this->storage_remove_service(std::move(local_service));
521  inner_service->exchange_in_use_by_wait_set_state(false);
522  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
523  });
524  }
525 
527 
550  void
552  std::shared_ptr<rclcpp::Waitable> waitable,
553  std::shared_ptr<void> associated_entity = nullptr)
554  {
555  if (nullptr == waitable) {
556  throw std::invalid_argument("waitable is nullptr");
557  }
558  // this method comes from the SynchronizationPolicy
559  this->sync_add_waitable(
560  std::move(waitable),
561  std::move(associated_entity),
562  [this](
563  std::shared_ptr<rclcpp::Waitable> && inner_waitable,
564  std::shared_ptr<void> && associated_entity)
565  {
566  bool already_in_use = inner_waitable->exchange_in_use_by_wait_set_state(true);
567  if (already_in_use) {
568  throw std::runtime_error("waitable already in use by another wait set");
569  }
570  // This method comes from the StoragePolicy, and it may not exist for
571  // fixed sized storage policies.
572  // It will throw if the waitable has already been added.
573  this->storage_add_waitable(std::move(inner_waitable), std::move(associated_entity));
574  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
575  });
576  }
577 
579 
587  void
588  remove_waitable(std::shared_ptr<rclcpp::Waitable> waitable)
589  {
590  if (nullptr == waitable) {
591  throw std::invalid_argument("waitable is nullptr");
592  }
593  // this method comes from the SynchronizationPolicy
594  this->sync_remove_waitable(
595  std::move(waitable),
596  [this](std::shared_ptr<rclcpp::Waitable> && inner_waitable) {
597  // This method comes from the StoragePolicy, and it may not exist for
598  // fixed sized storage policies.
599  // It will throw if the waitable is not in the wait set.
600  auto local_waitable = inner_waitable;
601  this->storage_remove_waitable(std::move(local_waitable));
602  inner_waitable->exchange_in_use_by_wait_set_state(false);
603  if (this->wait_result_holding_) {this->wait_result_dirty_ = true;}
604  });
605  }
606 
608 
623  void
625  {
626  // this method comes from the SynchronizationPolicy
627  this->sync_prune_deleted_entities(
628  [this]() {
629  // This method comes from the StoragePolicy, and it may not exist for
630  // fixed sized storage policies.
631  this->storage_prune_deleted_entities();
632  });
633  }
634 
636 
676  template<class Rep = int64_t, class Period = std::milli>
677  RCUTILS_WARN_UNUSED
679  wait(std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
680  {
681  auto time_to_wait_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(time_to_wait);
682 
683  // ensure the ownership of the entities in the wait set is shared for the duration of wait
684  this->storage_acquire_ownerships();
685  RCPPUTILS_SCOPE_EXIT({this->storage_release_ownerships();});
686 
687  // this method comes from the SynchronizationPolicy
688  return this->template sync_wait<WaitResult<WaitSetTemplate>>(
689  // pass along the time_to_wait duration as nanoseconds
690  time_to_wait_ns,
691  // this method provides the ability to rebuild the wait set, if needed
692  [this]() {
693  // This method comes from the StoragePolicy
694  this->storage_rebuild_rcl_wait_set(
695  // This method comes from the SynchronizationPolicy
696  this->get_extra_guard_conditions()
697  );
698  },
699  // this method provides access to the rcl wait set
700  [this]() -> rcl_wait_set_t & {
701  // This method comes from the StoragePolicy
702  return this->storage_get_rcl_wait_set();
703  },
704  // this method provides a way to create the WaitResult
705  [this](WaitResultKind wait_result_kind) -> WaitResult<WaitSetTemplate> {
706  // convert the result into a WaitResult
707  switch (wait_result_kind) {
708  case WaitResultKind::Ready:
710  case WaitResultKind::Timeout:
712  case WaitResultKind::Empty:
714  case WaitResultKind::Invalid:
715  auto msg = "invalid WaitResultKind with value: " + std::to_string(wait_result_kind);
716  throw std::runtime_error(msg);
717  }
718  // This should never be reached, but is needed to satisfy the compiler
719  throw std::runtime_error("unreachable code in wait result kind switch");
720  }
721  );
722  }
723 
724 private:
725  // Add WaitResult type as a friend so it can call private methods for
726  // acquiring and releasing resources as the WaitResult is initialized and
727  // destructed, respectively.
729 
731 
736  void
737  wait_result_acquire()
738  {
739  if (wait_result_holding_) {
740  throw std::runtime_error("wait_result_acquire() called while already holding");
741  }
742  wait_result_holding_ = true;
743  wait_result_dirty_ = false;
744  // this method comes from the SynchronizationPolicy
745  this->sync_wait_result_acquire();
746  }
747 
749 
754  void
755  wait_result_release()
756  {
757  if (!wait_result_holding_) {
758  throw std::runtime_error("wait_result_release() called while not holding");
759  }
760  wait_result_holding_ = false;
761  wait_result_dirty_ = false;
762  // this method comes from the SynchronizationPolicy
763  this->sync_wait_result_release();
764  }
765 
766  bool wait_result_holding_ = false;
767  bool wait_result_dirty_ = false;
768 };
769 
770 } // namespace rclcpp
771 
772 #endif // RCLCPP__WAIT_SET_TEMPLATE_HPP_
Options used to determine what parts of a subscription get added to or removed from a wait set.
bool include_events
If true, include any events attached to the subscription.
bool include_subscription
If true, include the actual subscription.
bool include_intra_process_waitable
If true, include the waitable used to handle intra process communication.
Interface for introspecting a wait set after waiting on it.
Definition: wait_result.hpp:62
Encapsulates sets of waitable items which can be waited on as a group.
void prune_deleted_entities()
Remove any destroyed entities from the wait set.
void add_guard_condition(std::shared_ptr< rclcpp::GuardCondition > guard_condition)
Add a guard condition to this wait set.
void remove_waitable(std::shared_ptr< rclcpp::Waitable > waitable)
Remove a waitable from this wait set.
void add_service(std::shared_ptr< rclcpp::ServiceBase > service)
Add a service to this wait set.
void add_subscription(std::shared_ptr< rclcpp::SubscriptionBase > subscription, rclcpp::SubscriptionWaitSetMask mask={})
Add a subscription to this wait set.
void remove_client(std::shared_ptr< rclcpp::ClientBase > client)
Remove a client from this wait set.
WaitSetTemplate(const typename StoragePolicy::SubscriptionsIterable &subscriptions={}, const typename StoragePolicy::GuardConditionsIterable &guard_conditions={}, const typename StoragePolicy::TimersIterable &timers={}, const typename StoragePolicy::ClientsIterable &clients={}, const typename StoragePolicy::ServicesIterable &services={}, const typename StoragePolicy::WaitablesIterable &waitables={}, rclcpp::Context::SharedPtr context=rclcpp::contexts::get_global_default_context())
Construct a wait set with optional initial waitable entities and optional custom context.
void add_waitable(std::shared_ptr< rclcpp::Waitable > waitable, std::shared_ptr< void > associated_entity=nullptr)
Add a waitable to this wait set.
void remove_subscription(std::shared_ptr< rclcpp::SubscriptionBase > subscription, rclcpp::SubscriptionWaitSetMask mask={})
Remove a subscription from this wait set.
void add_client(std::shared_ptr< rclcpp::ClientBase > client)
Add a client to this wait set.
void remove_guard_condition(std::shared_ptr< rclcpp::GuardCondition > guard_condition)
Remove a guard condition from this wait set.
void remove_timer(std::shared_ptr< rclcpp::TimerBase > timer)
Remove a timer from this wait set.
RCUTILS_WARN_UNUSED WaitResult< WaitSetTemplate > wait(std::chrono::duration< Rep, Period > time_to_wait=std::chrono::duration< Rep, Period >(-1))
Wait for any of the entities in the wait set to be ready, or a period of time to pass.
void add_timer(std::shared_ptr< rclcpp::TimerBase > timer)
Add a timer to this wait set.
void remove_service(std::shared_ptr< rclcpp::ServiceBase > service)
Remove a service from this wait set.
const rcl_wait_set_t & get_rcl_wait_set() const
Return the internal rcl wait set object.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:44