ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
ROS 2 C++ Client Library with ROS Client Library
storage_policy_common.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_POLICIES__DETAIL__STORAGE_POLICY_COMMON_HPP_
16 #define RCLCPP__WAIT_SET_POLICIES__DETAIL__STORAGE_POLICY_COMMON_HPP_
17 
18 #include <memory>
19 #include <stdexcept>
20 #include <utility>
21 
22 #include "rcl/wait.h"
23 
24 #include "rclcpp/exceptions.hpp"
25 #include "rclcpp/logging.hpp"
26 #include "rclcpp/macros.hpp"
27 #include "rclcpp/visibility_control.hpp"
28 #include "rclcpp/waitable.hpp"
29 
30 namespace rclcpp
31 {
32 namespace wait_set_policies
33 {
34 namespace detail
35 {
36 
38 template<bool HasStrongOwnership>
40 {
41 protected:
42  template<
43  class SubscriptionsIterable,
44  class GuardConditionsIterable,
45  class ExtraGuardConditionsIterable,
46  class TimersIterable,
47  class ClientsIterable,
48  class ServicesIterable,
49  class WaitablesIterable
50  >
51  explicit
53  const SubscriptionsIterable & subscriptions,
54  const GuardConditionsIterable & guard_conditions,
55  const ExtraGuardConditionsIterable & extra_guard_conditions,
56  const TimersIterable & timers,
57  const ClientsIterable & clients,
58  const ServicesIterable & services,
59  const WaitablesIterable & waitables,
60  rclcpp::Context::SharedPtr context
61  )
62  : rcl_wait_set_(rcl_get_zero_initialized_wait_set()), context_(context)
63  {
64  // Check context is not nullptr.
65  if (nullptr == context) {
66  throw std::invalid_argument("context is nullptr");
67  }
68  // Accumulate total contributions from waitables.
69  size_t subscriptions_from_waitables = 0;
70  size_t guard_conditions_from_waitables = 0;
71  size_t timers_from_waitables = 0;
72  size_t clients_from_waitables = 0;
73  size_t services_from_waitables = 0;
74  size_t events_from_waitables = 0;
75  for (const auto & waitable_entry : waitables) {
76  auto waitable_ptr_pair = get_raw_pointer_from_smart_pointer(waitable_entry.waitable);
77  if (nullptr == waitable_ptr_pair.second) {
78  if (HasStrongOwnership) {
79  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
80  }
81  // Flag for pruning.
82  needs_pruning_ = true;
83  continue;
84  }
85 
86  rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
87  subscriptions_from_waitables += waitable.get_number_of_ready_subscriptions();
88  guard_conditions_from_waitables += waitable.get_number_of_ready_guard_conditions();
89  timers_from_waitables += waitable.get_number_of_ready_timers();
90  clients_from_waitables += waitable.get_number_of_ready_clients();
91  services_from_waitables += waitable.get_number_of_ready_services();
92  events_from_waitables += waitable.get_number_of_ready_events();
93  }
94  // Initialize wait set using initial inputs.
96  &rcl_wait_set_,
97  subscriptions.size() + subscriptions_from_waitables,
98  guard_conditions.size() + extra_guard_conditions.size() + guard_conditions_from_waitables,
99  timers.size() + timers_from_waitables,
100  clients.size() + clients_from_waitables,
101  services.size() + services_from_waitables,
102  events_from_waitables,
103  context_->get_rcl_context().get(),
104  // TODO(wjwwood): support custom allocator, maybe restrict to polymorphic allocator
106  if (RCL_RET_OK != ret) {
107  rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to create wait set");
108  }
109 
110  // (Re)build the wait set for the first time.
112  subscriptions,
113  guard_conditions,
114  extra_guard_conditions,
115  timers,
116  clients,
117  services,
118  waitables);
119  }
120 
122  {
123  rcl_ret_t ret = rcl_wait_set_fini(&rcl_wait_set_);
124  if (RCL_RET_OK != ret) {
125  try {
126  rclcpp::exceptions::throw_from_rcl_error(ret);
127  } catch (const std::exception & exception) {
128  RCLCPP_ERROR(
129  rclcpp::get_logger("rclcpp"),
130  "Error in destruction of rcl wait set: %s", exception.what());
131  }
132  }
133  }
134 
135  template<class EntityT>
136  std::pair<void *, EntityT *>
137  get_raw_pointer_from_smart_pointer(const std::shared_ptr<EntityT> & shared_pointer)
138  {
139  return {nullptr, shared_pointer.get()};
140  }
141 
142  template<class EntityT>
143  std::pair<std::shared_ptr<EntityT>, EntityT *>
144  get_raw_pointer_from_smart_pointer(const std::weak_ptr<EntityT> & weak_pointer)
145  {
146  auto shared_pointer = weak_pointer.lock();
147  return {shared_pointer, shared_pointer.get()};
148  }
149 
151 
158  template<
159  class SubscriptionsIterable,
160  class GuardConditionsIterable,
161  class ExtraGuardConditionsIterable,
162  class TimersIterable,
163  class ClientsIterable,
164  class ServicesIterable,
165  class WaitablesIterable
166  >
167  void
169  const SubscriptionsIterable & subscriptions,
170  const GuardConditionsIterable & guard_conditions,
171  const ExtraGuardConditionsIterable & extra_guard_conditions,
172  const TimersIterable & timers,
173  const ClientsIterable & clients,
174  const ServicesIterable & services,
175  const WaitablesIterable & waitables
176  )
177  {
178  bool was_resized = false;
179  // Resize the wait set, but only if it needs to be.
180  if (needs_resize_) {
181  // Resizing with rcl_wait_set_resize() is a no-op if nothing has changed,
182  // but tracking the need to resize in this class avoids an unnecessary
183  // library call (rcl is most likely a separate shared library) each wait
184  // loop.
185  // Also, since static storage wait sets will never need resizing, so it
186  // avoids completely redundant calls to this function in that case.
187  // Accumulate total contributions from waitables.
188  size_t subscriptions_from_waitables = 0;
189  size_t guard_conditions_from_waitables = 0;
190  size_t timers_from_waitables = 0;
191  size_t clients_from_waitables = 0;
192  size_t services_from_waitables = 0;
193  size_t events_from_waitables = 0;
194  for (const auto & waitable_entry : waitables) {
195  if (!waitable_entry.waitable) {
196  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
197  if (HasStrongOwnership) {
198  // This will not happen in fixed sized storage, as it holds
199  // shared ownership the whole time and is never in need of pruning.
200  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
201  }
202  // Flag for pruning.
203  needs_pruning_ = true;
204  continue;
205  }
206  auto & waitable = *waitable_entry.waitable;
207  subscriptions_from_waitables += waitable.get_number_of_ready_subscriptions();
208  guard_conditions_from_waitables += waitable.get_number_of_ready_guard_conditions();
209  timers_from_waitables += waitable.get_number_of_ready_timers();
210  clients_from_waitables += waitable.get_number_of_ready_clients();
211  services_from_waitables += waitable.get_number_of_ready_services();
212  events_from_waitables += waitable.get_number_of_ready_events();
213  }
215  &rcl_wait_set_,
216  subscriptions.size() + subscriptions_from_waitables,
217  guard_conditions.size() + extra_guard_conditions.size() + guard_conditions_from_waitables,
218  timers.size() + timers_from_waitables,
219  clients.size() + clients_from_waitables,
220  services.size() + services_from_waitables,
221  events_from_waitables
222  );
223  if (RCL_RET_OK != ret) {
224  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't resize the wait set");
225  }
226  was_resized = true;
227  // Assumption: the calling code ensures this function is not called
228  // concurrently with functions that set this variable to true, either
229  // with documentation (as is the case for the SequentialSychronization
230  // policy), or with synchronization primitives (as is the case with
231  // the ThreadSafeSynchronization policy).
232  needs_resize_ = false;
233  }
234 
235  // Now clear the wait set, but only if it was not resized, as resizing also
236  // clears the wait set.
237  if (!was_resized) {
238  rcl_ret_t ret = rcl_wait_set_clear(&rcl_wait_set_);
239  if (RCL_RET_OK != ret) {
240  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't clear the wait set");
241  }
242  }
243 
244  // Add subscriptions.
245  for (const auto & subscription_entry : subscriptions) {
246  if (!subscription_entry.subscription) {
247  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
248  if (HasStrongOwnership) {
249  // This will not happen in fixed sized storage, as it holds
250  // shared ownership the whole time and is never in need of pruning.
251  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
252  }
253  // Flag for pruning.
254  needs_pruning_ = true;
255  continue;
256  }
257 
259  &rcl_wait_set_,
260  subscription_entry.subscription->get_subscription_handle().get(),
261  nullptr);
262  if (RCL_RET_OK != ret) {
263  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
264  }
265  }
266 
267  // Setup common code to add guard_conditions.
268  auto add_guard_conditions =
269  [this](const auto & inner_guard_conditions)
270  {
271  for (const auto & guard_condition : inner_guard_conditions) {
272  if (!guard_condition) {
273  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
274  if (HasStrongOwnership) {
275  // This will not happen in fixed sized storage, as it holds
276  // shared ownership the whole time and is never in need of pruning.
277  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
278  }
279  // Flag for pruning.
280  needs_pruning_ = true;
281  continue;
282  }
284  &rcl_wait_set_,
285  &guard_condition->get_rcl_guard_condition(),
286  nullptr);
287  if (RCL_RET_OK != ret) {
288  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
289  }
290  }
291  };
292 
293  // Add guard conditions.
294  add_guard_conditions(guard_conditions);
295 
296  // Add extra guard conditions.
297  add_guard_conditions(extra_guard_conditions);
298 
299  // Add timers.
300  for (const auto & timer : timers) {
301  if (!timer) {
302  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
303  if (HasStrongOwnership) {
304  // This will not happen in fixed sized storage, as it holds
305  // shared ownership the whole time and is never in need of pruning.
306  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
307  }
308  // Flag for pruning.
309  needs_pruning_ = true;
310  continue;
311  }
313  &rcl_wait_set_,
314  timer->get_timer_handle().get(),
315  nullptr);
316  if (RCL_RET_OK != ret) {
317  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
318  }
319  }
320 
321  // Add clients.
322  for (const auto & client : clients) {
323  if (!client) {
324  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
325  if (HasStrongOwnership) {
326  // This will not happen in fixed sized storage, as it holds
327  // shared ownership the whole time and is never in need of pruning.
328  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
329  }
330  // Flag for pruning.
331  needs_pruning_ = true;
332  continue;
333  }
335  &rcl_wait_set_,
336  client->get_client_handle().get(),
337  nullptr);
338  if (RCL_RET_OK != ret) {
339  rclcpp::exceptions::throw_from_rcl_error(ret);
340  }
341  }
342 
343  // Add services.
344  for (const auto & service : services) {
345  if (!service) {
346  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
347  if (HasStrongOwnership) {
348  // This will not happen in fixed sized storage, as it holds
349  // shared ownership the whole time and is never in need of pruning.
350  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
351  }
352  // Flag for pruning.
353  needs_pruning_ = true;
354  continue;
355  }
357  &rcl_wait_set_,
358  service->get_service_handle().get(),
359  nullptr);
360  if (RCL_RET_OK != ret) {
361  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
362  }
363  }
364 
365  // Add waitables.
366  for (auto & waitable_entry : waitables) {
367  if (!waitable_entry.waitable) {
368  // In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
369  if (HasStrongOwnership) {
370  // This will not happen in fixed sized storage, as it holds
371  // shared ownership the whole time and is never in need of pruning.
372  throw std::runtime_error("unexpected condition, fixed storage policy needs pruning");
373  }
374  // Flag for pruning.
375  needs_pruning_ = true;
376  continue;
377  }
378  waitable_entry.waitable->add_to_wait_set(rcl_wait_set_);
379  }
380  }
381 
382  const rcl_wait_set_t &
383  storage_get_rcl_wait_set() const
384  {
385  return rcl_wait_set_;
386  }
387 
389  storage_get_rcl_wait_set()
390  {
391  return rcl_wait_set_;
392  }
393 
394  void
395  storage_flag_for_resize()
396  {
397  needs_resize_ = true;
398  }
399 
400  size_t size_of_subscriptions() const {return 0;}
401  size_t size_of_timers() const {return 0;}
402  size_t size_of_clients() const {return 0;}
403  size_t size_of_services() const {return 0;}
404  size_t size_of_waitables() const {return 0;}
405 
406  template<class SubscriptionsIterable>
407  typename SubscriptionsIterable::value_type
408  subscriptions(size_t) const {return nullptr;}
409 
410  template<class TimersIterable>
411  typename TimersIterable::value_type
412  timers(size_t) const {return nullptr;}
413 
414  template<class ClientsIterable>
415  typename ClientsIterable::value_type
416  clients(size_t) const {return nullptr;}
417 
418  template<class ServicesIterable>
419  typename ServicesIterable::value_type
420  services(size_t) const {return nullptr;}
421 
422  template<class WaitablesIterable>
423  typename WaitablesIterable::value_type
424  waitables(size_t) const {return nullptr;}
425 
426  rcl_wait_set_t rcl_wait_set_;
427  rclcpp::Context::SharedPtr context_;
428 
429  bool needs_pruning_ = false;
430  bool needs_resize_ = false;
431 };
432 
433 } // namespace detail
434 } // namespace wait_set_policies
435 } // namespace rclcpp
436 
437 #endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__STORAGE_POLICY_COMMON_HPP_
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
virtual RCLCPP_PUBLIC size_t get_number_of_ready_guard_conditions()
Get the number of ready guard_conditions.
Definition: waitable.cpp:52
virtual RCLCPP_PUBLIC size_t get_number_of_ready_timers()
Get the number of ready timers.
Definition: waitable.cpp:28
virtual RCLCPP_PUBLIC size_t get_number_of_ready_clients()
Get the number of ready clients.
Definition: waitable.cpp:34
virtual RCLCPP_PUBLIC size_t get_number_of_ready_events()
Get the number of ready events.
Definition: waitable.cpp:40
virtual RCLCPP_PUBLIC size_t get_number_of_ready_subscriptions()
Get the number of ready subscriptions.
Definition: waitable.cpp:22
virtual RCLCPP_PUBLIC size_t get_number_of_ready_services()
Get the number of ready services.
Definition: waitable.cpp:46
Common structure for storage policies, which provides rcl wait set access.
void storage_rebuild_rcl_wait_set_with_sets(const SubscriptionsIterable &subscriptions, const GuardConditionsIterable &guard_conditions, const ExtraGuardConditionsIterable &extra_guard_conditions, const TimersIterable &timers, const ClientsIterable &clients, const ServicesIterable &services, const WaitablesIterable &waitables)
Rebuild the wait set, preparing it for the next wait call.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:42
#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
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_subscription(rcl_wait_set_t *wait_set, const rcl_subscription_t *subscription, size_t *index)
Store a pointer to the given subscription in the next empty spot in the set.
Definition: wait.c:305
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_service(rcl_wait_set_t *wait_set, const rcl_service_t *service, size_t *index)
Store a pointer to the service in the next empty spot in the set.
Definition: wait.c:486
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_init(rcl_wait_set_t *wait_set, size_t number_of_subscriptions, size_t number_of_guard_conditions, size_t number_of_timers, size_t number_of_clients, size_t number_of_services, size_t number_of_events, rcl_context_t *context, rcl_allocator_t allocator)
Initialize a rcl wait set with space for items to be waited on.
Definition: wait.c:78
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_clear(rcl_wait_set_t *wait_set)
Remove (sets to NULL) all entities in the wait set.
Definition: wait.c:321
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_timer(rcl_wait_set_t *wait_set, const rcl_timer_t *timer, size_t *index)
Store a pointer to the timer in the next empty spot in the set.
Definition: wait.c:455
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_client(rcl_wait_set_t *wait_set, const rcl_client_t *client, size_t *index)
Store a pointer to the client in the next empty spot in the set.
Definition: wait.c:475
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_fini(rcl_wait_set_t *wait_set)
Finalize a rcl wait set.
Definition: wait.c:167
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_resize(rcl_wait_set_t *wait_set, size_t subscriptions_size, size_t guard_conditions_size, size_t timers_size, size_t clients_size, size_t services_size, size_t events_size)
Reallocate space for entities in the wait set.
Definition: wait.c:363
RCL_PUBLIC RCL_WARN_UNUSED rcl_wait_set_t rcl_get_zero_initialized_wait_set(void)
Return a rcl_wait_set_t struct with members set to NULL.
Definition: wait.c:64
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait_set_add_guard_condition(rcl_wait_set_t *wait_set, const rcl_guard_condition_t *guard_condition, size_t *index)
Store a pointer to the guard condition in the next empty spot in the set.
Definition: wait.c:441