ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
allocator_memory_strategy.hpp
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 #ifndef RCLCPP__STRATEGIES__ALLOCATOR_MEMORY_STRATEGY_HPP_
16 #define RCLCPP__STRATEGIES__ALLOCATOR_MEMORY_STRATEGY_HPP_
17 
18 #include <memory>
19 #include <vector>
20 
21 #include "rcl/allocator.h"
22 
23 #include "rclcpp/allocator/allocator_common.hpp"
24 #include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
25 #include "rclcpp/memory_strategy.hpp"
26 #include "rclcpp/node.hpp"
27 #include "rclcpp/visibility_control.hpp"
28 
29 #include "rcutils/logging_macros.h"
30 
31 #include "rmw/types.h"
32 
33 namespace rclcpp
34 {
35 namespace memory_strategies
36 {
37 namespace allocator_memory_strategy
38 {
39 
41 
48 template<typename Alloc = std::allocator<void>>
49 class [[deprecated("The executor does not used this anymore")]] AllocatorMemoryStrategy : public
51 {
52 public:
53  RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>)
54 
55  using VoidAllocTraits = typename allocator::AllocRebind<void *, Alloc>;
56  using VoidAlloc = typename VoidAllocTraits::allocator_type;
57 
58  explicit AllocatorMemoryStrategy(std::shared_ptr<Alloc> allocator)
59  {
60  allocator_ = std::make_shared<VoidAlloc>(*allocator.get());
61  }
62 
64  {
65  allocator_ = std::make_shared<VoidAlloc>();
66  }
67 
68  void add_guard_condition(const rclcpp::GuardCondition & guard_condition) override
69  {
70  for (const auto & existing_guard_condition : guard_conditions_) {
71  if (existing_guard_condition == &guard_condition) {
72  return;
73  }
74  }
75  guard_conditions_.push_back(&guard_condition);
76  }
77 
78  void remove_guard_condition(const rclcpp::GuardCondition * guard_condition) override
79  {
80  for (auto it = guard_conditions_.begin(); it != guard_conditions_.end(); ++it) {
81  if (*it == guard_condition) {
82  guard_conditions_.erase(it);
83  break;
84  }
85  }
86  }
87 
88  void clear_handles() override
89  {
90  subscription_handles_.clear();
91  service_handles_.clear();
92  client_handles_.clear();
93  timer_handles_.clear();
94  waitable_handles_.clear();
95  }
96 
97  void remove_null_handles(rcl_wait_set_t * wait_set) override
98  {
99  // TODO(jacobperron): Check if wait set sizes are what we expect them to be?
100  // e.g. wait_set->size_of_clients == client_handles_.size()
101 
102  // Important to use subscription_handles_.size() instead of wait set's size since
103  // there may be more subscriptions in the wait set due to Waitables added to the end.
104  // The same logic applies for other entities.
105 
106  // Mark corresponding weak_ptr as expired for entities that are null in the wait set
107  size_t valid_subscription_count = 0;
108  for (size_t i = 0; i < subscription_handles_.size(); ++i) {
109  if (valid_subscription_count < wait_set->size_of_subscriptions &&
110  !wait_set->subscriptions[valid_subscription_count])
111  {
112  subscription_handles_[i] = std::weak_ptr<const rcl_subscription_t>{};
113  }
114  if (subscription_handles_[i].lock()) {
115  ++valid_subscription_count;
116  }
117  }
118 
119  size_t valid_service_count = 0;
120  for (size_t i = 0; i < service_handles_.size(); ++i) {
121  if (valid_service_count < wait_set->size_of_services &&
122  !wait_set->services[valid_service_count])
123  {
124  service_handles_[i] = std::weak_ptr<const rcl_service_t>{};
125  }
126  if (service_handles_[i].lock()) {
127  ++valid_service_count;
128  }
129  }
130 
131  size_t valid_client_count = 0;
132  for (size_t i = 0; i < client_handles_.size(); ++i) {
133  if (valid_client_count < wait_set->size_of_clients &&
134  !wait_set->clients[valid_client_count])
135  {
136  client_handles_[i] = std::weak_ptr<const rcl_client_t>{};
137  }
138  if (client_handles_[i].lock()) {
139  ++valid_client_count;
140  }
141  }
142 
143  size_t valid_timer_count = 0;
144  for (size_t i = 0; i < timer_handles_.size(); ++i) {
145  if (valid_timer_count < wait_set->size_of_timers &&
146  !wait_set->timers[valid_timer_count])
147  {
148  timer_handles_[i] = std::weak_ptr<const rcl_timer_t>{};
149  }
150  if (timer_handles_[i].lock()) {
151  ++valid_timer_count;
152  }
153  }
154 
155  for (size_t i = 0; i < waitable_handles_.size(); ++i) {
156  if (!waitable_handles_[i]->is_ready(*wait_set)) {
157  waitable_handles_[i].reset();
158  }
159  }
160 
161  // Remove expired weak_ptr instances
162  subscription_handles_.erase(
163  std::remove_if(subscription_handles_.begin(), subscription_handles_.end(),
164  [](const std::weak_ptr<const rcl_subscription_t> & weak_ptr) {
165  return weak_ptr.expired();
166  }),
167  subscription_handles_.end()
168  );
169 
170  service_handles_.erase(
171  std::remove_if(service_handles_.begin(), service_handles_.end(),
172  [](const std::weak_ptr<const rcl_service_t> & weak_ptr) {
173  return weak_ptr.expired();
174  }),
175  service_handles_.end()
176  );
177 
178  client_handles_.erase(
179  std::remove_if(client_handles_.begin(), client_handles_.end(),
180  [](const std::weak_ptr<const rcl_client_t> & weak_ptr) {
181  return weak_ptr.expired();
182  }),
183  client_handles_.end()
184  );
185 
186  timer_handles_.erase(
187  std::remove_if(timer_handles_.begin(), timer_handles_.end(),
188  [](const std::weak_ptr<const rcl_timer_t> & weak_ptr) {
189  return weak_ptr.expired();
190  }),
191  timer_handles_.end()
192  );
193 
194  waitable_handles_.erase(
195  std::remove(waitable_handles_.begin(), waitable_handles_.end(), nullptr),
196  waitable_handles_.end()
197  );
198  }
199 
200  bool collect_entities(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
201  {
202  bool has_invalid_weak_groups_or_nodes = false;
203  for (const auto & pair : weak_groups_to_nodes) {
204  auto group = pair.first.lock();
205  auto node = pair.second.lock();
206  if (group == nullptr || node == nullptr) {
207  has_invalid_weak_groups_or_nodes = true;
208  continue;
209  }
210  if (!group || !group->can_be_taken_from().load()) {
211  continue;
212  }
213 
214  group->collect_all_ptrs(
215  [this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
216  subscription_handles_.push_back(subscription->get_subscription_handle());
217  },
218  [this](const rclcpp::ServiceBase::SharedPtr & service) {
219  service_handles_.push_back(service->get_service_handle());
220  },
221  [this](const rclcpp::ClientBase::SharedPtr & client) {
222  client_handles_.push_back(client->get_client_handle());
223  },
224  [this](const rclcpp::TimerBase::SharedPtr & timer) {
225  timer_handles_.push_back(timer->get_timer_handle());
226  },
227  [this](const rclcpp::Waitable::SharedPtr & waitable) {
228  waitable_handles_.push_back(waitable);
229  });
230  }
231 
232  return has_invalid_weak_groups_or_nodes;
233  }
234 
235  void add_waitable_handle(const rclcpp::Waitable::SharedPtr & waitable) override
236  {
237  if (nullptr == waitable) {
238  throw std::runtime_error("waitable object unexpectedly nullptr");
239  }
240  waitable_handles_.push_back(waitable);
241  }
242 
243  bool add_handles_to_wait_set(rcl_wait_set_t * wait_set) override
244  {
245  for (const std::weak_ptr<const rcl_subscription_t> & weak_subscription :
246  subscription_handles_)
247  {
248  auto subscription = weak_subscription.lock();
249  if (!subscription) {
250  continue; // Skip expired handles
251  }
252  if (rcl_wait_set_add_subscription(wait_set, subscription.get(), NULL) != RCL_RET_OK) {
253  RCUTILS_LOG_ERROR_NAMED(
254  "rclcpp",
255  "Couldn't add subscription to wait set: %s", rcl_get_error_string().str);
256  rcl_reset_error();
257  return false;
258  }
259  }
260 
261  for (const std::weak_ptr<const rcl_client_t> & weak_client : client_handles_) {
262  auto client = weak_client.lock();
263  if (!client) {
264  continue; // Skip expired handles
265  }
266  if (rcl_wait_set_add_client(wait_set, client.get(), NULL) != RCL_RET_OK) {
267  RCUTILS_LOG_ERROR_NAMED(
268  "rclcpp",
269  "Couldn't add client to wait set: %s", rcl_get_error_string().str);
270  rcl_reset_error();
271  return false;
272  }
273  }
274 
275  for (const std::weak_ptr<const rcl_service_t> & weak_service : service_handles_) {
276  auto service = weak_service.lock();
277  if (!service) {
278  continue; // Skip expired handles
279  }
280  if (rcl_wait_set_add_service(wait_set, service.get(), NULL) != RCL_RET_OK) {
281  RCUTILS_LOG_ERROR_NAMED(
282  "rclcpp",
283  "Couldn't add service to wait set: %s", rcl_get_error_string().str);
284  rcl_reset_error();
285  return false;
286  }
287  }
288 
289  for (const std::weak_ptr<const rcl_timer_t> & weak_timer : timer_handles_) {
290  auto timer = weak_timer.lock();
291  if (!timer) {
292  continue; // Skip expired handles
293  }
294  if (rcl_wait_set_add_timer(wait_set, timer.get(), NULL) != RCL_RET_OK) {
295  RCUTILS_LOG_ERROR_NAMED(
296  "rclcpp",
297  "Couldn't add timer to wait set: %s", rcl_get_error_string().str);
298  rcl_reset_error();
299  return false;
300  }
301  }
302 
303  for (auto guard_condition : guard_conditions_) {
304  detail::add_guard_condition_to_rcl_wait_set(*wait_set, *guard_condition);
305  }
306 
307  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
308  waitable->add_to_wait_set(*wait_set);
309  }
310  return true;
311  }
312 
313  void
314  get_next_subscription(
315  rclcpp::AnyExecutable & any_exec,
316  const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
317  {
318  auto it = subscription_handles_.begin();
319  while (it != subscription_handles_.end()) {
320  auto subscription_handle = it->lock();
321  if (!subscription_handle) {
322  // Handle expired, remove it and continue
323  it = subscription_handles_.erase(it);
324  continue;
325  }
326  auto subscription = get_subscription_by_handle(subscription_handle, weak_groups_to_nodes);
327  if (subscription) {
328  // Find the group for this handle and see if it can be serviced
329  auto group = get_group_by_subscription(subscription, weak_groups_to_nodes);
330  if (!group) {
331  // Group was not found, meaning the subscription is not valid...
332  // Remove it from the ready list and continue looking
333  it = subscription_handles_.erase(it);
334  continue;
335  }
336  if (!group->can_be_taken_from().load()) {
337  // Group is mutually exclusive and is being used, so skip it for now
338  // Leave it to be checked next time, but continue searching
339  ++it;
340  continue;
341  }
342  // Otherwise it is safe to set and return the any_exec
343  any_exec.subscription = subscription;
344  any_exec.callback_group = group;
345  any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
346  subscription_handles_.erase(it);
347  return;
348  }
349  // Else, the subscription is no longer valid, remove it and continue
350  it = subscription_handles_.erase(it);
351  }
352  }
353 
354  void
355  get_next_service(
356  rclcpp::AnyExecutable & any_exec,
357  const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
358  {
359  auto it = service_handles_.begin();
360  while (it != service_handles_.end()) {
361  auto service_handle = it->lock();
362  if (!service_handle) {
363  // Handle expired, remove it and continue
364  it = service_handles_.erase(it);
365  continue;
366  }
367  auto service = get_service_by_handle(service_handle, weak_groups_to_nodes);
368  if (service) {
369  // Find the group for this handle and see if it can be serviced
370  auto group = get_group_by_service(service, weak_groups_to_nodes);
371  if (!group) {
372  // Group was not found, meaning the service is not valid...
373  // Remove it from the ready list and continue looking
374  it = service_handles_.erase(it);
375  continue;
376  }
377  if (!group->can_be_taken_from().load()) {
378  // Group is mutually exclusive and is being used, so skip it for now
379  // Leave it to be checked next time, but continue searching
380  ++it;
381  continue;
382  }
383  // Otherwise it is safe to set and return the any_exec
384  any_exec.service = service;
385  any_exec.callback_group = group;
386  any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
387  service_handles_.erase(it);
388  return;
389  }
390  // Else, the service is no longer valid, remove it and continue
391  it = service_handles_.erase(it);
392  }
393  }
394 
395  void
396  get_next_client(
397  rclcpp::AnyExecutable & any_exec,
398  const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
399  {
400  auto it = client_handles_.begin();
401  while (it != client_handles_.end()) {
402  auto client_handle = it->lock();
403  if (!client_handle) {
404  // Handle expired, remove it and continue
405  it = client_handles_.erase(it);
406  continue;
407  }
408  auto client = get_client_by_handle(client_handle, weak_groups_to_nodes);
409  if (client) {
410  // Find the group for this handle and see if it can be serviced
411  auto group = get_group_by_client(client, weak_groups_to_nodes);
412  if (!group) {
413  // Group was not found, meaning the service is not valid...
414  // Remove it from the ready list and continue looking
415  it = client_handles_.erase(it);
416  continue;
417  }
418  if (!group->can_be_taken_from().load()) {
419  // Group is mutually exclusive and is being used, so skip it for now
420  // Leave it to be checked next time, but continue searching
421  ++it;
422  continue;
423  }
424  // Otherwise it is safe to set and return the any_exec
425  any_exec.client = client;
426  any_exec.callback_group = group;
427  any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
428  client_handles_.erase(it);
429  return;
430  }
431  // Else, the service is no longer valid, remove it and continue
432  it = client_handles_.erase(it);
433  }
434  }
435 
436  void
437  get_next_timer(
438  rclcpp::AnyExecutable & any_exec,
439  const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
440  {
441  auto it = timer_handles_.begin();
442  while (it != timer_handles_.end()) {
443  auto timer_handle = it->lock();
444  if (!timer_handle) {
445  // Handle expired, remove it and continue
446  it = timer_handles_.erase(it);
447  continue;
448  }
449  auto timer = get_timer_by_handle(timer_handle, weak_groups_to_nodes);
450  if (timer) {
451  // Find the group for this handle and see if it can be serviced
452  auto group = get_group_by_timer(timer, weak_groups_to_nodes);
453  if (!group) {
454  // Group was not found, meaning the timer is not valid...
455  // Remove it from the ready list and continue looking
456  it = timer_handles_.erase(it);
457  continue;
458  }
459  if (!group->can_be_taken_from().load()) {
460  // Group is mutually exclusive and is being used, so skip it for now
461  // Leave it to be checked next time, but continue searching
462  ++it;
463  continue;
464  }
465  auto data = timer->call();
466  if (!data) {
467  // timer was cancelled, skip it.
468  ++it;
469  continue;
470  }
471  // Otherwise it is safe to set and return the any_exec
472  any_exec.timer = timer;
473  any_exec.callback_group = group;
474  any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
475  any_exec.data = data;
476  timer_handles_.erase(it);
477  return;
478  }
479  // Else, the timer is no longer valid, remove it and continue
480  it = timer_handles_.erase(it);
481  }
482  }
483 
484  void
485  get_next_waitable(
486  rclcpp::AnyExecutable & any_exec,
487  const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
488  {
489  auto it = waitable_handles_.begin();
490  while (it != waitable_handles_.end()) {
491  std::shared_ptr<Waitable> & waitable = *it;
492  if (waitable) {
493  // Find the group for this handle and see if it can be serviced
494  auto group = get_group_by_waitable(waitable, weak_groups_to_nodes);
495  if (!group) {
496  // Group was not found, meaning the waitable is not valid...
497  // Remove it from the ready list and continue looking
498  it = waitable_handles_.erase(it);
499  continue;
500  }
501  if (!group->can_be_taken_from().load()) {
502  // Group is mutually exclusive and is being used, so skip it for now
503  // Leave it to be checked next time, but continue searching
504  ++it;
505  continue;
506  }
507  // Otherwise it is safe to set and return the any_exec
508  any_exec.waitable = waitable;
509  any_exec.callback_group = group;
510  any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
511  waitable_handles_.erase(it);
512  return;
513  }
514  // Else, the waitable is no longer valid, remove it and continue
515  it = waitable_handles_.erase(it);
516  }
517  }
518 
519  rcl_allocator_t get_allocator() override
520  {
521  if constexpr (std::is_same_v<Alloc, std::allocator<void>>) {
522  return rcl_get_default_allocator();
523  } else {
524  return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
525  }
526  }
527 
528  size_t number_of_ready_subscriptions() const override
529  {
530  size_t number_of_subscriptions = 0;
531  // Count only non-expired weak_ptr references
532  for (const auto & weak_subscription : subscription_handles_) {
533  if (!weak_subscription.expired()) {
534  ++number_of_subscriptions;
535  }
536  }
537  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
538  number_of_subscriptions += waitable->get_number_of_ready_subscriptions();
539  }
540  return number_of_subscriptions;
541  }
542 
543  size_t number_of_ready_services() const override
544  {
545  size_t number_of_services = 0;
546  // Count only non-expired weak_ptr references
547  for (const auto & weak_service : service_handles_) {
548  if (!weak_service.expired()) {
549  ++number_of_services;
550  }
551  }
552  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
553  number_of_services += waitable->get_number_of_ready_services();
554  }
555  return number_of_services;
556  }
557 
558  size_t number_of_ready_events() const override
559  {
560  size_t number_of_events = 0;
561  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
562  number_of_events += waitable->get_number_of_ready_events();
563  }
564  return number_of_events;
565  }
566 
567  size_t number_of_ready_clients() const override
568  {
569  size_t number_of_clients = 0;
570  // Count only non-expired weak_ptr references
571  for (const auto & weak_client : client_handles_) {
572  if (!weak_client.expired()) {
573  ++number_of_clients;
574  }
575  }
576  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
577  number_of_clients += waitable->get_number_of_ready_clients();
578  }
579  return number_of_clients;
580  }
581 
582  size_t number_of_guard_conditions() const override
583  {
584  size_t number_of_guard_conditions = guard_conditions_.size();
585  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
586  number_of_guard_conditions += waitable->get_number_of_ready_guard_conditions();
587  }
588  return number_of_guard_conditions;
589  }
590 
591  size_t number_of_ready_timers() const override
592  {
593  size_t number_of_timers = 0;
594  // Count only non-expired weak_ptr references
595  for (const auto & weak_timer : timer_handles_) {
596  if (!weak_timer.expired()) {
597  ++number_of_timers;
598  }
599  }
600  for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
601  number_of_timers += waitable->get_number_of_ready_timers();
602  }
603  return number_of_timers;
604  }
605 
606  size_t number_of_waitables() const override
607  {
608  return waitable_handles_.size();
609  }
610 
611 private:
612  template<typename T>
613  using VectorRebind =
614  std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
615 
616  VectorRebind<const rclcpp::GuardCondition *> guard_conditions_;
617 
618  VectorRebind<std::weak_ptr<const rcl_subscription_t>> subscription_handles_;
619  VectorRebind<std::weak_ptr<const rcl_service_t>> service_handles_;
620  VectorRebind<std::weak_ptr<const rcl_client_t>> client_handles_;
621  VectorRebind<std::weak_ptr<const rcl_timer_t>> timer_handles_;
622  VectorRebind<std::shared_ptr<Waitable>> waitable_handles_;
623 
624  std::shared_ptr<VoidAlloc> allocator_;
625 };
626 
627 } // namespace allocator_memory_strategy
628 } // namespace memory_strategies
629 } // namespace rclcpp
630 
631 #endif // RCLCPP__STRATEGIES__ALLOCATOR_MEMORY_STRATEGY_HPP_
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
A condition that can be waited on in a single wait set and asynchronously triggered.
Delegate for handling memory allocations while the Executor is executing.
Delegate for handling memory allocations while the Executor is executing.
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:42
const rcl_timer_t ** timers
Storage for timer pointers.
Definition: wait.h:52
const rcl_service_t ** services
Storage for service pointers.
Definition: wait.h:60
const rcl_client_t ** clients
Storage for client pointers.
Definition: wait.h:56
const rcl_subscription_t ** subscriptions
Storage for subscription pointers.
Definition: wait.h:44
#define RCL_RET_OK
Success return code.
Definition: types.h:27
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:334
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:515
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:484
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:504