ROS 2 rclcpp + rcl - jazzy  jazzy
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)
63  : scheduler(scheduler) {}
64 
65  CallbackGroupHandle(const CallbackGroupHandle &) = delete;
67 
68  virtual ~CallbackGroupHandle() = default;
69 
70  CallbackGroupHandle & operator=(const CallbackGroupHandle &) = delete;
71  CallbackGroupHandle & operator=(CallbackGroupHandle &&) = delete;
72 
73  virtual std::function<void(size_t)> get_ready_callback_for_entity(
74  const rclcpp::SubscriptionBase::WeakPtr & entity) = 0;
75  virtual std::function<void(std::function<void()> executed_callback)>
76  get_ready_callback_for_entity(const rclcpp::TimerBase::WeakPtr & entity) = 0;
77  virtual std::function<void(size_t)> get_ready_callback_for_entity(
78  const rclcpp::ClientBase::WeakPtr & entity) = 0;
79  virtual std::function<void(size_t)> get_ready_callback_for_entity(
80  const rclcpp::ServiceBase::WeakPtr & entity) = 0;
81  virtual std::function<void(size_t,
82  int)> get_ready_callback_for_entity(const rclcpp::Waitable::WeakPtr & entity) = 0;
83  virtual std::function<void(size_t)> get_ready_callback_for_entity(
84  const CallbackEventType & entity) = 0;
85 
90  {
91  {
92  std::lock_guard l(ready_mutex);
93  not_ready = false;
94 
95  if(!has_ready_entities()) {
96  idle = true;
97  return;
98  }
99  }
100  // inform scheduler that we have more work
101  scheduler.callback_group_ready(this, false);
102  }
103 
104  bool is_ready();
105 
106 protected:
107  CBGScheduler & scheduler;
108 
112  virtual bool has_ready_entities() const = 0;
113 
121  template<typename add_fun>
122  void add_ready_entity(const add_fun & fun)
123  {
124  {
125  std::lock_guard l(ready_mutex);
126 
127  fun();
128 
129  if(not_ready || !idle) {
130  return;
131  }
132 
133  idle = false;
134  }
135 
136  // If we reached this point, we were idle and now have work,
137  // therefore we need to move this callback group into the list
138  // of ready callback groups.
139  scheduler.callback_group_ready(this, true);
140  }
141 
142  void mark_as_skipped()
143  {
144  if(!has_ready_entities()) {
145  idle = true;
146  }
147  }
148 
155  {
156  not_ready = true;
157  }
158 
159  std::mutex ready_mutex;
160 
161 private:
162  // will be set if cbg is mutual exclusive and something is executing
163  bool not_ready = false;
164 
165  // true, if nothing is beeing executed, and there are no pending events
166  bool idle = true;
167  };
168 
170  {
171  // if called executes the entity
172  std::function<void()> execute_function;
173  // The callback group associated with the entity. Can be nullptr.
174  CallbackGroupHandle *callback_handle = nullptr;
175  };
176 
182  explicit CBGScheduler(std::function<void ()> sync_function)
183  : sync_function(sync_function) {}
184  CBGScheduler(const CBGScheduler &) = delete;
185  CBGScheduler(CBGScheduler &&) = delete;
186  virtual ~CBGScheduler() = default;
187 
188  CBGScheduler & operator=(const CBGScheduler &) = delete;
189  CBGScheduler & operator=(CBGScheduler &&) = delete;
190 
191  CallbackGroupHandle * add_callback_group(const rclcpp::CallbackGroup::SharedPtr & callback_group)
192  {
193  auto uPtr = get_handle_for_callback_group(callback_group);
194  CallbackGroupHandle * ret = uPtr.get();
195 
196  std::lock_guard lk(ready_callback_groups_mutex);
197 
198  callback_groups.push_back(std::move(uPtr));
199  return ret;
200  }
201 
202  void remove_callback_group(const CallbackGroupHandle *callback_handle)
203  {
204  std::lock_guard lk(ready_callback_groups_mutex);
205  ready_callback_groups.erase(std::find(ready_callback_groups.begin(),
206  ready_callback_groups.end(), callback_handle));
207 
208  callback_groups.remove_if([&callback_handle] (const auto & e) {
209  return e.get() == callback_handle;
210  });
211  }
212 
219  void callback_group_ready(CallbackGroupHandle *handle, bool callback_group_was_idle)
220  {
221  {
222  std::lock_guard l(ready_callback_groups_mutex);
223  ready_callback_groups.push_back(handle);
224  }
225 
226  if(callback_group_was_idle) {
228  }
229  }
230 
232  {
233  std::optional<ExecutableEntity> entity;
234  bool moreEntitiesReady{};
235  };
236 
244  {
245  {
246  std::lock_guard l(ready_callback_groups_mutex);
247  if(needs_sync) {
248  needs_sync = false;
250  ExecutableEntity{sync_function, nullptr},
251  false};
252  }
253  }
254 
255  return get_next_ready_entity_intern();
256  }
257 
258  ExecutableEntityWithInfo get_next_ready_entity(
259  GlobalEventIdProvider::MonotonicId max_id)
260  {
261  {
262  std::lock_guard l(ready_callback_groups_mutex);
263  if(needs_sync) {
264  needs_sync = false;
265  return ExecutableEntityWithInfo{
266  ExecutableEntity{sync_function, nullptr},
267  false};
268  }
269  }
270 
271  return get_next_ready_entity_intern(max_id);
272  }
273 
274  virtual ExecutableEntityWithInfo get_next_ready_entity_intern() = 0;
275  virtual ExecutableEntityWithInfo get_next_ready_entity_intern(
276  GlobalEventIdProvider::MonotonicId max_id) = 0;
277 
287  {
288  bool wake_worker = false;
289  {
290  std::lock_guard l(ready_callback_groups_mutex);
291  if(!needs_sync) {
292  wake_worker = true;
293  }
294  needs_sync = true;
295  }
296  if(wake_worker) {
298  }
299  }
300 
307  {
308  if(e.callback_handle != nullptr) {
309  e.callback_handle->mark_as_executed();
310  }
311  }
312 
317  {
318  {
319  std::lock_guard lk(ready_callback_groups_mutex);
320  release_worker_once = true;
321  }
322  work_ready_conditional.notify_one();
323  }
324 
325  void block_worker_thread()
326  {
327  std::unique_lock lk(ready_callback_groups_mutex);
328  work_ready_conditional.wait(lk, [this]() -> bool {
329  return !ready_callback_groups.empty() || release_worker_once || release_workers;
330  });
331  release_worker_once = false;
332  }
333 
334  void block_worker_thread_for(std::chrono::nanoseconds timeout)
335  {
336  std::unique_lock lk(ready_callback_groups_mutex);
337  work_ready_conditional.wait_for(lk, timeout, [this]() -> bool {
338  return !ready_callback_groups.empty() || release_worker_once || release_workers;
339  });
340  release_worker_once = false;
341  }
342 
343  void release_all_worker_threads()
344  {
345  {
346  std::lock_guard lk(ready_callback_groups_mutex);
347  release_workers = true;
348  }
349  work_ready_conditional.notify_all();
350  }
351 
352 protected:
353  virtual std::unique_ptr<CallbackGroupHandle> get_handle_for_callback_group(
354  const rclcpp::CallbackGroup::SharedPtr & callback_group) = 0;
355 
356  // sync function, will be triggered if the executor needs
357  // resync. E.g. if entities / cbg or nodes were added / removed
358  std::function<void ()> sync_function;
359 
360  bool needs_sync = false;
361 
362  std::mutex ready_callback_groups_mutex;
363  std::deque<CallbackGroupHandle *> ready_callback_groups;
364 
365  bool release_workers = false;
366  bool release_worker_once = false;
367 
368  std::condition_variable work_ready_conditional;
369 
370  std::list<std::unique_ptr<CallbackGroupHandle>> callback_groups;
371 };
372 } // namespace cbg_executor
373 } // namespace executors
374 } // namespace rclcpp
void callback_group_ready(CallbackGroupHandle *handle, bool callback_group_was_idle)
Definition: scheduler.hpp:219
void mark_entity_as_executed(const ExecutableEntity &e)
Definition: scheduler.hpp:306
ExecutableEntityWithInfo get_next_ready_entity()
Definition: scheduler.hpp:243
CBGScheduler(std::function< void()> sync_function)
Definition: scheduler.hpp:182
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.