ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
executor_entities_collector.cpp
1 // Copyright 2023 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 <set>
16 
17 #include "rclcpp/executors/executor_entities_collector.hpp"
18 #include "rclcpp/executors/executor_notify_waitable.hpp"
19 #include "rclcpp/node_interfaces/node_base_interface.hpp"
20 
21 namespace rclcpp
22 {
23 namespace executors
24 {
25 
27  const std::shared_ptr<ExecutorNotifyWaitable> & notify_waitable)
28 : notify_waitable_(notify_waitable)
29 {
30 }
31 
33 {
34  for (auto weak_node_it = weak_nodes_.begin(); weak_node_it != weak_nodes_.end(); ) {
35  weak_node_it = remove_weak_node(weak_node_it);
36  }
37 
38  for (auto weak_group_it = automatically_added_groups_.begin();
39  weak_group_it != automatically_added_groups_.end(); )
40  {
41  weak_group_it = remove_weak_callback_group(weak_group_it, automatically_added_groups_);
42  }
43 
44  for (auto weak_group_it = manually_added_groups_.begin();
45  weak_group_it != manually_added_groups_.end(); )
46  {
47  weak_group_it = remove_weak_callback_group(weak_group_it, manually_added_groups_);
48  }
49 
50  for (const auto & weak_node_ptr : pending_added_nodes_) {
51  auto node_ptr = weak_node_ptr.lock();
52  if (node_ptr) {
53  node_ptr->get_associated_with_executor_atomic().store(false);
54  }
55  }
56  pending_added_nodes_.clear();
57  pending_removed_nodes_.clear();
58 
59  for (const auto & weak_group_ptr : pending_manually_added_groups_) {
60  auto group_ptr = weak_group_ptr.lock();
61  if (group_ptr) {
62  group_ptr->get_associated_with_executor_atomic().store(false);
63  }
64  // Disassociate the guard condition from the executor notify waitable
65  auto guard_condition_it = weak_groups_to_guard_conditions_.find(weak_group_ptr);
66  if (guard_condition_it != weak_groups_to_guard_conditions_.end()) {
67  this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
68  weak_groups_to_guard_conditions_.erase(guard_condition_it);
69  }
70  }
71  pending_manually_added_groups_.clear();
72  pending_manually_removed_groups_.clear();
73 }
74 
75 bool
77 {
78  std::lock_guard<std::mutex> lock(mutex_);
79  return pending_manually_added_groups_.size() != 0 ||
80  pending_manually_removed_groups_.size() != 0 ||
81  pending_added_nodes_.size() != 0 ||
82  pending_removed_nodes_.size() != 0;
83 }
84 
85 void
87  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr)
88 {
89  // If the node already has an executor
90  std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
91  if (has_executor.exchange(true)) {
92  throw std::runtime_error(
93  std::string("Node '") + node_ptr->get_fully_qualified_name() +
94  "' has already been added to an executor.");
95  }
96 
97  std::lock_guard<std::mutex> lock(mutex_);
98  bool associated = weak_nodes_.count(node_ptr) != 0;
99  bool add_queued = pending_added_nodes_.count(node_ptr) != 0;
100  bool remove_queued = pending_removed_nodes_.count(node_ptr) != 0;
101 
102  if ((associated || add_queued) && !remove_queued) {
103  throw std::runtime_error(
104  std::string("Node '") + node_ptr->get_fully_qualified_name() +
105  "' has already been added to this executor.");
106  }
107 
108  this->pending_added_nodes_.insert(node_ptr);
109 }
110 
111 void
113  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr)
114 {
115  std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
116  if (!has_executor.exchange(false)) {
117  throw std::runtime_error(
118  std::string("Node '") + node_ptr->get_fully_qualified_name() +
119  "' needs to be associated with an executor.");
120  }
121 
122  std::lock_guard<std::mutex> lock(mutex_);
123  bool associated = weak_nodes_.count(node_ptr) != 0;
124  bool add_queued = pending_added_nodes_.count(node_ptr) != 0;
125  bool remove_queued = pending_removed_nodes_.count(node_ptr) != 0;
126 
127  if (!(associated || add_queued) || remove_queued) {
128  throw std::runtime_error(
129  std::string("Node '") + node_ptr->get_fully_qualified_name() +
130  "' needs to be associated with this executor.");
131  }
132 
133  this->pending_removed_nodes_.insert(node_ptr);
134 }
135 
136 void
137 ExecutorEntitiesCollector::add_callback_group(const rclcpp::CallbackGroup::SharedPtr & group_ptr)
138 {
139  std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
140  if (has_executor.exchange(true)) {
141  throw std::runtime_error("Callback group has already been added to an executor.");
142  }
143 
144  std::lock_guard<std::mutex> lock(mutex_);
145  bool associated = manually_added_groups_.count(group_ptr) != 0;
146  bool add_queued = pending_manually_added_groups_.count(group_ptr) != 0;
147  bool remove_queued = pending_manually_removed_groups_.count(group_ptr) != 0;
148 
149  if ((associated || add_queued) && !remove_queued) {
150  throw std::runtime_error("Callback group has already been added to this executor.");
151  }
152 
153  this->pending_manually_added_groups_.insert(group_ptr);
154 
155  // Store callback group notify guard condition in map and add it to the notify waitable
156  auto group_guard_condition = group_ptr->get_notify_guard_condition();
157  weak_groups_to_guard_conditions_.insert({group_ptr, group_guard_condition});
158  this->notify_waitable_->add_guard_condition(group_guard_condition);
159 }
160 
161 void
162 ExecutorEntitiesCollector::remove_callback_group(const rclcpp::CallbackGroup::SharedPtr & group_ptr)
163 {
164  if (!group_ptr->get_associated_with_executor_atomic().load()) {
165  throw std::runtime_error("Callback group needs to be associated with an executor.");
166  }
177  auto weak_group_ptr = rclcpp::CallbackGroup::WeakPtr(group_ptr);
178  std::lock_guard<std::mutex> lock(mutex_);
179  bool associated = manually_added_groups_.count(group_ptr) != 0;
180  bool add_queued = pending_manually_added_groups_.count(group_ptr) != 0;
181  bool remove_queued = pending_manually_removed_groups_.count(group_ptr) != 0;
182 
183  if (!(associated || add_queued) || remove_queued) {
184  throw std::runtime_error("Callback group needs to be associated with this executor.");
185  }
186 
187  this->pending_manually_removed_groups_.insert(group_ptr);
188 }
189 
190 std::vector<rclcpp::CallbackGroup::WeakPtr>
192 {
193  std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
194  std::lock_guard<std::mutex> lock(mutex_);
195  for (const auto & group_ptr : manually_added_groups_) {
196  groups.push_back(group_ptr);
197  }
198  for (auto const & group_ptr : automatically_added_groups_) {
199  groups.push_back(group_ptr);
200  }
201  return groups;
202 }
203 
204 std::vector<rclcpp::CallbackGroup::WeakPtr>
206 {
207  std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
208  std::lock_guard<std::mutex> lock(mutex_);
209  for (const auto & group_ptr : manually_added_groups_) {
210  groups.push_back(group_ptr);
211  }
212  return groups;
213 }
214 
215 std::vector<rclcpp::CallbackGroup::WeakPtr>
217 {
218  std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
219  std::lock_guard<std::mutex> lock(mutex_);
220  for (auto const & group_ptr : automatically_added_groups_) {
221  groups.push_back(group_ptr);
222  }
223  return groups;
224 }
225 
226 void
228 {
229  std::lock_guard<std::mutex> lock(mutex_);
230  this->process_queues();
231  this->add_automatically_associated_callback_groups(this->weak_nodes_);
233 }
234 
235 ExecutorEntitiesCollector::NodeCollection::iterator
236 ExecutorEntitiesCollector::remove_weak_node(NodeCollection::iterator weak_node)
237 {
238  // Disassociate the guard condition from the executor notify waitable
239  auto guard_condition_it = weak_nodes_to_guard_conditions_.find(*weak_node);
240  if (guard_condition_it != weak_nodes_to_guard_conditions_.end()) {
241  this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
242  weak_nodes_to_guard_conditions_.erase(guard_condition_it);
243  }
244 
245  // Mark the node as disassociated (if the node is still valid)
246  auto node_ptr = weak_node->lock();
247  if (node_ptr) {
248  std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
249  has_executor.store(false);
250  }
251 
252  // Remove the node from tracked nodes
253  return weak_nodes_.erase(weak_node);
254 }
255 
256 ExecutorEntitiesCollector::CallbackGroupCollection::iterator
258  CallbackGroupCollection::iterator weak_group_it,
259  CallbackGroupCollection & collection
260 )
261 {
262  // Disassociate the guard condition from the executor notify waitable
263  auto guard_condition_it = weak_groups_to_guard_conditions_.find(*weak_group_it);
264  if (guard_condition_it != weak_groups_to_guard_conditions_.end()) {
265  this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
266  weak_groups_to_guard_conditions_.erase(guard_condition_it);
267  }
268 
269  // Mark the node as disassociated (if the group is still valid)
270  auto group_ptr = weak_group_it->lock();
271  if (group_ptr) {
282  std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
283  has_executor.store(false);
284  }
285 
286  // Remove the node from tracked nodes
287  return collection.erase(weak_group_it);
288 }
289 
290 void
292  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
293  CallbackGroupCollection & collection)
294 {
295  auto iter = collection.insert(group_ptr);
296  if (iter.second == false) {
297  throw std::runtime_error("Callback group has already been added to this executor.");
298  }
299 
300  // Store node guard condition in map and add it to the notify waitable
301  auto group_guard_condition = group_ptr->get_notify_guard_condition();
302  weak_groups_to_guard_conditions_.insert({group_ptr, group_guard_condition});
303  this->notify_waitable_->add_guard_condition(group_guard_condition);
304 }
305 
306 void
308 {
309  for (const auto & weak_node_ptr : pending_added_nodes_) {
310  auto node_ptr = weak_node_ptr.lock();
311  if (!node_ptr) {
312  continue;
313  }
314  weak_nodes_.insert(weak_node_ptr);
315  this->add_automatically_associated_callback_groups({weak_node_ptr});
316 
317  // Store node guard condition in map and add it to the notify waitable
318  auto node_guard_condition = node_ptr->get_shared_notify_guard_condition();
319  weak_nodes_to_guard_conditions_.insert({weak_node_ptr, node_guard_condition});
320  this->notify_waitable_->add_guard_condition(node_guard_condition);
321  }
322  pending_added_nodes_.clear();
323 
324  for (const auto & weak_node_ptr : pending_removed_nodes_) {
325  auto node_it = weak_nodes_.find(weak_node_ptr);
326  if (node_it != weak_nodes_.end()) {
327  remove_weak_node(node_it);
328  } else {
329  // The node may have been destroyed and removed from the colletion before
330  // we processed the queues. Don't throw if the pointer is already expired.
331  if (!weak_node_ptr.expired()) {
332  throw std::runtime_error("Node needs to be associated with this executor.");
333  }
334  }
335 
336  auto node_ptr = weak_node_ptr.lock();
337  if (node_ptr) {
338  for (auto group_it = automatically_added_groups_.begin();
339  group_it != automatically_added_groups_.end(); )
340  {
341  auto group_ptr = group_it->lock();
342  if (node_ptr->callback_group_in_node(group_ptr)) {
343  group_it = remove_weak_callback_group(group_it, automatically_added_groups_);
344  } else {
345  ++group_it;
346  }
347  }
348  }
349  }
350  pending_removed_nodes_.clear();
351 
352  for (const auto & weak_group_ptr : pending_manually_added_groups_) {
353  auto group_ptr = weak_group_ptr.lock();
354  if (group_ptr) {
355  this->add_callback_group_to_collection(group_ptr, manually_added_groups_);
356  } else {
357  // Disassociate the guard condition from the executor notify waitable
358  auto guard_condition_it = weak_groups_to_guard_conditions_.find(weak_group_ptr);
359  if (guard_condition_it != weak_groups_to_guard_conditions_.end()) {
360  this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
361  weak_groups_to_guard_conditions_.erase(guard_condition_it);
362  }
363  }
364  }
365  pending_manually_added_groups_.clear();
366 
367  for (const auto & weak_group_ptr : pending_manually_removed_groups_) {
368  auto group_ptr = weak_group_ptr.lock();
369  if (group_ptr) {
370  auto group_it = manually_added_groups_.find(group_ptr);
371  if (group_it != manually_added_groups_.end()) {
372  remove_weak_callback_group(group_it, manually_added_groups_);
373  } else {
374  throw std::runtime_error(
375  "Attempting to remove a callback group not added to this executor.");
376  }
377  }
378  }
379  pending_manually_removed_groups_.clear();
380 }
381 
382 void
384  const NodeCollection & nodes_to_check)
385 {
386  for (auto & weak_node : nodes_to_check) {
387  auto node = weak_node.lock();
388  if (node) {
389  node->for_each_callback_group(
390  [this, node](const rclcpp::CallbackGroup::SharedPtr & group_ptr)
391  {
392  if (!group_ptr->get_associated_with_executor_atomic().load() &&
393  group_ptr->automatically_add_to_executor_with_node())
394  {
395  std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
396  if (has_executor.exchange(true)) {
397  throw std::runtime_error("Callback group has already been added to an executor.");
398  }
399  this->add_callback_group_to_collection(group_ptr, this->automatically_added_groups_);
400  }
401  });
402  }
403  }
404 }
405 
406 void
407 ExecutorEntitiesCollector::prune_invalid_nodes_and_groups()
408 {
409  for (auto node_it = weak_nodes_.begin();
410  node_it != weak_nodes_.end(); )
411  {
412  if (node_it->expired()) {
413  node_it = remove_weak_node(node_it);
414  } else {
415  node_it++;
416  }
417  }
418  for (auto group_it = automatically_added_groups_.begin();
419  group_it != automatically_added_groups_.end(); )
420  {
421  if (group_it->expired()) {
422  group_it = remove_weak_callback_group(group_it, automatically_added_groups_);
423  } else {
424  group_it++;
425  }
426  }
427  for (auto group_it = manually_added_groups_.begin();
428  group_it != manually_added_groups_.end(); )
429  {
430  if (group_it->expired()) {
431  group_it = remove_weak_callback_group(group_it, manually_added_groups_);
432  } else {
433  group_it++;
434  }
435  }
436 }
437 
438 } // namespace executors
439 } // namespace rclcpp
RCLCPP_PUBLIC NodeCollection::iterator remove_weak_node(NodeCollection::iterator weak_node) RCPPUTILS_TSA_REQUIRES(mutex_)
Implementation of removing a node from the collector.
RCLCPP_PUBLIC void add_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr)
Add a node to the entity collector.
RCLCPP_PUBLIC void add_callback_group_to_collection(const rclcpp::CallbackGroup::SharedPtr &group_ptr, CallbackGroupCollection &collection) RCPPUTILS_TSA_REQUIRES(mutex_)
Implementation of adding a callback group.
RCLCPP_PUBLIC void prune_invalid_nodes_and_groups() RCPPUTILS_TSA_REQUIRES(mutex_)
Check all nodes and group for expired weak pointers and remove them.
std::shared_ptr< ExecutorNotifyWaitable > notify_waitable_
Waitable to add guard conditions to.
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.
bool has_pending() const
Indicate if the entities collector has pending additions or removals.
RCLCPP_PUBLIC void process_queues() RCPPUTILS_TSA_REQUIRES(mutex_)
Iterate over queued added/remove nodes and callback_groups.
RCLCPP_PUBLIC void add_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr)
Add a callback group to the entity collector.
RCLCPP_PUBLIC ExecutorEntitiesCollector(const std::shared_ptr< ExecutorNotifyWaitable > &notify_waitable)
Constructor.
RCLCPP_PUBLIC void remove_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr)
Remove a callback group from the entity collector.
std::mutex mutex_
mutex to protect collections and pending queues
RCLCPP_PUBLIC void add_automatically_associated_callback_groups(const NodeCollection &nodes_to_check) RCPPUTILS_TSA_REQUIRES(mutex_)
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 CallbackGroupCollection::iterator remove_weak_callback_group(CallbackGroupCollection::iterator weak_group_it, CallbackGroupCollection &collection) RCPPUTILS_TSA_REQUIRES(mutex_)
Implementation of removing a callback group from the 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.