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