ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
scheduler.hpp
1 // Copyright 2024 Cellumation GmbH.
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 #pragma once
15 
16 #include <deque>
17 #include <functional>
18 #include <list>
19 #include <memory>
20 #include <utility>
21 
22 #include <rclcpp/callback_group.hpp>
23 #include "global_event_id_provider.hpp"
24 
25 namespace rclcpp
26 {
27 namespace executors
28 {
29 namespace cbg_executor
30 {
32 {
33 public:
35  {
36  rclcpp::Waitable::WeakPtr waitable;
37  int internal_event_type;
38 
39  bool expired() const
40  {
41  return waitable.expired();
42  }
43  };
44 
46  {
47  explicit CallbackEventType(std::function<void()> callback)
48  : callback(std::move(callback))
49  {
50  }
51 
52  std::function<void()> callback;
53 
54  bool expired() const
55  {
56  return false;
57  }
58  };
59 
61  {
62  explicit CallbackGroupHandle(CBGScheduler & scheduler, CallbackGroupType type)
63  : scheduler(scheduler), type(type)
64  {
65  }
66 
67  CallbackGroupHandle(const CallbackGroupHandle &) = delete;
69 
70  virtual ~CallbackGroupHandle() = default;
71 
72  CallbackGroupHandle & operator=(const CallbackGroupHandle &) = delete;
73  CallbackGroupHandle & operator=(CallbackGroupHandle &&) = delete;
74 
75  virtual std::function<void(size_t)> get_ready_callback_for_entity(
76  const rclcpp::SubscriptionBase::WeakPtr & entity) = 0;
77  virtual std::function<void(std::function<void()> executed_callback)>
78  get_ready_callback_for_entity(const rclcpp::TimerBase::WeakPtr & entity) = 0;
79  virtual std::function<void(size_t)> get_ready_callback_for_entity(
80  const rclcpp::ClientBase::WeakPtr & entity) = 0;
81  virtual std::function<void(size_t)> get_ready_callback_for_entity(
82  const rclcpp::ServiceBase::WeakPtr & entity) = 0;
83  virtual std::function<void(size_t,
84  int)> get_ready_callback_for_entity(const rclcpp::Waitable::WeakPtr & entity) = 0;
85  virtual std::function<void(size_t)> get_ready_callback_for_entity(
86  const CallbackEventType & entity) = 0;
87 
92  {
93  {
94  std::lock_guard l(ready_mutex);
95  not_ready = false;
96 
97  if(!has_ready_entities()) {
98  idle = true;
99  return;
100  }
101  }
102  // inform scheduler that we have more work
103  scheduler.callback_group_ready(this, false);
104  }
105 
106  CallbackGroupType get_type() {return type;}
107 
108  bool is_ready();
109 
110  // true if this cbg is inside the scheduler's queue
111  bool in_queue = false;
112 
113 protected:
114  CBGScheduler & scheduler;
115 
119  virtual bool has_ready_entities() const = 0;
120 
128  template<typename add_fun>
129  void add_ready_entity(const add_fun & fun)
130  {
131  {
132  std::lock_guard l(ready_mutex);
133 
134  fun();
135 
136  if(not_ready || !idle) {
137  return;
138  }
139 
140  idle = false;
141  }
142 
143  // If we reached this point, we were idle and now have work,
144  // therefore we need to move this callback group into the list
145  // of ready callback groups.
146  scheduler.callback_group_ready(this, true);
147  }
148 
149  void mark_as_skipped()
150  {
151  if(!has_ready_entities()) {
152  idle = true;
153  }
154  }
155 
162  {
163  if (type != CallbackGroupType::Reentrant) {
164  not_ready = true;
165  }
166  }
167 
168  std::mutex ready_mutex;
169 
170 private:
171  // will be set if cbg is mutual exclusive and something is executing
172  bool not_ready = false;
173 
174  // true, if nothing is beeing executed, and there are no pending events
175  bool idle = true;
176 
177  // type of the underlying callback group
178  CallbackGroupType type;
179  };
180 
182  {
183  // if called executes the entity
184  std::function<void()> execute_function;
185  // The callback group associated with the entity. Can be nullptr.
186  CallbackGroupHandle *callback_handle = nullptr;
187  };
188 
194  explicit CBGScheduler(std::function<void ()> sync_function)
195  : sync_function(sync_function) {}
196  CBGScheduler(const CBGScheduler &) = delete;
197  CBGScheduler(CBGScheduler &&) = delete;
198  virtual ~CBGScheduler() = default;
199 
200  CBGScheduler & operator=(const CBGScheduler &) = delete;
201  CBGScheduler & operator=(CBGScheduler &&) = delete;
202 
203  CallbackGroupHandle * add_callback_group(const rclcpp::CallbackGroup::SharedPtr & callback_group)
204  {
205  auto uPtr = get_handle_for_callback_group(callback_group);
206  CallbackGroupHandle * ret = uPtr.get();
207 
208  std::lock_guard lk(ready_callback_groups_mutex);
209 
210  callback_groups.push_back(std::move(uPtr));
211  return ret;
212  }
213 
214  void remove_callback_group(const CallbackGroupHandle *callback_handle)
215  {
216  std::lock_guard lk(ready_callback_groups_mutex);
217  ready_callback_groups.erase(std::find(ready_callback_groups.begin(),
218  ready_callback_groups.end(), callback_handle));
219 
220  callback_groups.remove_if([&callback_handle] (const auto & e) {
221  return e.get() == callback_handle;
222  });
223  }
224 
231  void callback_group_ready(CallbackGroupHandle *handle, bool callback_group_was_idle)
232  {
233  if (!handle->in_queue) {
234  std::lock_guard l(ready_callback_groups_mutex);
235 
236  ready_callback_groups.push_back(handle);
237  handle->in_queue = true;
238  }
239 
240  if(callback_group_was_idle) {
242  }
243  }
244 
246  {
247  std::optional<ExecutableEntity> entity;
248  bool moreEntitiesReady{};
249  };
250 
258  {
259  {
260  std::lock_guard l(ready_callback_groups_mutex);
261  if(needs_sync) {
262  needs_sync = false;
263  return ExecutableEntityWithInfo{.entity =
264  ExecutableEntity{.execute_function = sync_function, .callback_handle = nullptr},
265  .moreEntitiesReady = false};
266  }
267  }
268 
269  return get_next_ready_entity_intern();
270  }
271 
272  ExecutableEntityWithInfo get_next_ready_entity(
273  GlobalEventIdProvider::MonotonicId max_id)
274  {
275  {
276  std::lock_guard l(ready_callback_groups_mutex);
277  if(needs_sync) {
278  needs_sync = false;
279  return ExecutableEntityWithInfo{.entity =
280  ExecutableEntity{.execute_function = sync_function, .callback_handle = nullptr},
281  .moreEntitiesReady = false};
282  }
283  }
284 
285  return get_next_ready_entity_intern(max_id);
286  }
287 
288  virtual ExecutableEntityWithInfo get_next_ready_entity_intern() = 0;
289  virtual ExecutableEntityWithInfo get_next_ready_entity_intern(
290  GlobalEventIdProvider::MonotonicId max_id) = 0;
291 
301  {
302  bool wake_worker = false;
303  {
304  std::lock_guard l(ready_callback_groups_mutex);
305  if(!needs_sync) {
306  wake_worker = true;
307  }
308  needs_sync = true;
309  }
310  if(wake_worker) {
312  }
313  }
314 
321  {
322  if(e.callback_handle != nullptr) {
323  e.callback_handle->mark_as_executed();
324  }
325  }
326 
331  {
332  {
333  std::lock_guard lk(ready_callback_groups_mutex);
334  release_worker_once = true;
335  }
336  work_ready_conditional.notify_one();
337  }
338 
339  void block_worker_thread()
340  {
341  std::unique_lock lk(ready_callback_groups_mutex);
342  work_ready_conditional.wait(lk, [this]() -> bool {
343  return !ready_callback_groups.empty() || release_worker_once || release_workers;
344  });
345  release_worker_once = false;
346  }
347 
348  void block_worker_thread_for(std::chrono::nanoseconds timeout)
349  {
350  std::unique_lock lk(ready_callback_groups_mutex);
351  work_ready_conditional.wait_for(lk, timeout, [this]() -> bool {
352  return !ready_callback_groups.empty() || release_worker_once || release_workers;
353  });
354  release_worker_once = false;
355  }
356 
357  void release_all_worker_threads()
358  {
359  {
360  std::lock_guard lk(ready_callback_groups_mutex);
361  release_workers = true;
362  }
363  work_ready_conditional.notify_all();
364  }
365 
366 protected:
367  virtual std::unique_ptr<CallbackGroupHandle> get_handle_for_callback_group(
368  const rclcpp::CallbackGroup::SharedPtr & callback_group) = 0;
369 
370  // sync function, will be triggered if the executor needs
371  // resync. E.g. if entities / cbg or nodes were added / removed
372  std::function<void ()> sync_function;
373 
374  bool needs_sync = false;
375 
376  std::mutex ready_callback_groups_mutex;
377  std::deque<CallbackGroupHandle *> ready_callback_groups;
378 
379  bool release_workers = false;
380  bool release_worker_once = false;
381 
382  std::condition_variable work_ready_conditional;
383 
384  std::list<std::unique_ptr<CallbackGroupHandle>> callback_groups;
385 };
386 } // namespace cbg_executor
387 } // namespace executors
388 } // namespace rclcpp
void callback_group_ready(CallbackGroupHandle *handle, bool callback_group_was_idle)
Definition: scheduler.hpp:231
void mark_entity_as_executed(const ExecutableEntity &e)
Definition: scheduler.hpp:320
ExecutableEntityWithInfo get_next_ready_entity()
Definition: scheduler.hpp:257
CBGScheduler(std::function< void()> sync_function)
Definition: scheduler.hpp:194
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.