ROS 2 rclcpp + rcl - rolling  rolling-e47c0848
ROS 2 C++ Client Library with ROS Client Library
events_cbg_executor.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 
15 #pragma once
16 
17 #include <chrono>
18 #include <memory>
19 #include <mutex>
20 #include <thread>
21 #include <vector>
22 
23 #include "rclcpp/executor.hpp"
24 #include "rclcpp/macros.hpp"
25 #include "rclcpp/utilities.hpp"
26 #include "rclcpp/visibility_control.hpp"
27 
28 namespace rclcpp
29 {
30 namespace executors
31 {
32 
33 namespace cbg_executor
34 {
35 class TimerManager;
36 struct RegisteredEntityCache;
37 class CBGScheduler;
38 struct GlobalWeakExecutableCache;
39 }
40 
42 {
43 public:
44  RCLCPP_SMART_PTR_DEFINITIONS(EventsCBGExecutor)
45 
46 
58  RCLCPP_PUBLIC
59  explicit EventsCBGExecutor(
61  size_t number_of_threads = 0,
62  std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
63 
64  RCLCPP_PUBLIC
65  virtual ~EventsCBGExecutor();
66 
67  RCLCPP_PUBLIC
68  void
70  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
71  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
72  bool notify = true) override;
73 
74  RCLCPP_PUBLIC
75  std::vector<rclcpp::CallbackGroup::WeakPtr>
76  get_all_callback_groups() override;
77 
78  RCLCPP_PUBLIC
79  std::vector<rclcpp::CallbackGroup::WeakPtr>
81 
82  RCLCPP_PUBLIC
83  std::vector<rclcpp::CallbackGroup::WeakPtr>
85 
86  RCLCPP_PUBLIC
87  void
89  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
90  bool notify = true) override;
91 
92  RCLCPP_PUBLIC
93  void
94  add_node(
95  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
96  bool notify = true) override;
97 
99 
102  RCLCPP_PUBLIC
103  void
104  add_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify = true) override;
105 
106  RCLCPP_PUBLIC
107  void
108  remove_node(
109  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
110  bool notify = true) override;
111 
113 
116  RCLCPP_PUBLIC
117  void
118  remove_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify = true) override;
119 
120  // add a callback group to the executor, not bound to any node
121  void add_callback_group_only(const rclcpp::CallbackGroup::SharedPtr & group_ptr);
122 
127  RCLCPP_PUBLIC
128  void
129  spin() override;
130 
139  RCLCPP_PUBLIC
140  void
141  spin(const std::function<void(const std::exception &)> & exception_handler);
142 
143  RCLCPP_PUBLIC
144  void
145  spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)) override;
146 
147  RCLCPP_PUBLIC
148  void
149  spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
150 
154  RCLCPP_PUBLIC
156  std::chrono::nanoseconds max_duration,
157  bool recollect_if_no_work_available);
158 
159  RCLCPP_PUBLIC
160  void
161  spin_all(std::chrono::nanoseconds max_duration) override;
162 
164 
168  RCLCPP_PUBLIC
169  void
170  cancel() override;
171 
172  RCLCPP_PUBLIC
173  size_t
174  get_number_of_threads() const;
175 
176  bool
177  is_spinning()
178  {
179  return spinning;
180  }
181 
182  template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
184  spin_until_future_complete(
185  const FutureT & future,
186  std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
187  {
188  // TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
189  // inside a callback executed by an executor.
190 
191  // Check the future before entering the while loop.
192  // If the future is already complete, don't try to spin.
193  std::future_status status = future.wait_for(std::chrono::seconds(0));
194  if (status == std::future_status::ready) {
195  return FutureReturnCode::SUCCESS;
196  }
197 
198  auto end_time = std::chrono::steady_clock::now();
199  std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
200  timeout);
201  if (timeout_ns > std::chrono::nanoseconds::zero()) {
202  end_time += timeout_ns;
203  }
204  std::chrono::nanoseconds timeout_left = timeout_ns;
205 
206  if (spinning.exchange(true)) {
207  throw std::runtime_error("spin_until_future_complete() called while already spinning");
208  }
209  RCPPUTILS_SCOPE_EXIT(
210  this->spinning.store(false);
211  this->cancel_requested_.store(false); );
212  if (cancel_requested_.load()) {
213  return FutureReturnCode::INTERRUPTED;
214  }
215  while (rclcpp::ok(this->context_) && !cancel_requested_.load()) {
216  // Do one item of work.
217  spin_once_internal(timeout_left);
218 
219  // Check if the future is set, return SUCCESS if it is.
220  status = future.wait_for(std::chrono::seconds(0));
221  if (status == std::future_status::ready) {
222  return FutureReturnCode::SUCCESS;
223  }
224  // If the original timeout is < 0, then this is blocking, never TIMEOUT.
225  if (timeout_ns < std::chrono::nanoseconds::zero()) {
226  continue;
227  }
228  // Otherwise check if we still have time to wait, return TIMEOUT if not.
229  auto now = std::chrono::steady_clock::now();
230  if (now >= end_time) {
231  return FutureReturnCode::TIMEOUT;
232  }
233  // Subtract the elapsed time from the original timeout.
234  timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
235  }
236 
237  // The future did not complete before ok() returned false, return INTERRUPTED.
238  return FutureReturnCode::INTERRUPTED;
239  }
240 
246 
247 protected:
248  RCLCPP_PUBLIC
249  void
250  run(size_t this_thread_number, bool block_initially);
251 
252  RCLCPP_PUBLIC
253  void
254  run(
255  size_t this_thread_number,
256  const std::function<void(const std::exception &)> & exception_handler);
257 
262  void shutdown();
263 
264  std::unique_ptr<cbg_executor::CBGScheduler> scheduler;
265 
267  {
268  enum class Origin
269  {
270  Node,
271  ManualAdded,
272  };
273 
274  CallbackGroup::WeakPtr callback_group;
275 
276  std::unique_ptr<cbg_executor::RegisteredEntityCache> registered_entities;
277 
278  Origin origin;
279  };
280 
281  void set_callbacks(CallbackGroupData & cgd);
282 
288  const std::chrono::time_point<std::chrono::steady_clock> & stop_time);
289 
290  void unregister_event_callbacks(const rclcpp::CallbackGroup::SharedPtr & cbg) const;
291 
292 private:
293  void remove_all_nodes_and_callback_groups();
294 
295  void sync_callback_groups();
296 
301  void trigger_callback_group_sync();
302 
303  RCLCPP_PUBLIC
304  void spin_once_internal(std::chrono::nanoseconds timeout);
305 
306  RCLCPP_DISABLE_COPY(EventsCBGExecutor)
307 
308  std::mutex added_callback_groups_mutex_;
309  std::vector<rclcpp::CallbackGroup::WeakPtr> added_callback_groups;
310 
311  std::mutex added_nodes_mutex_;
312  std::vector<node_interfaces::NodeBaseInterface::WeakPtr> added_nodes;
313 
314  std::mutex callback_groups_mutex;
315 
316  std::vector<CallbackGroupData> callback_groups;
317 
318  size_t number_of_threads_;
319 
320  std::chrono::nanoseconds next_exec_timeout_;
321 
322  std::atomic_bool needs_callback_group_resync = false;
323 
325 
330  std::atomic_bool spinning;
331 
333  bool in_shutdown = false;
334 
336  std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
337 
339  std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
340 
342  rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
343 
345  std::shared_ptr<rclcpp::Context> context_;
346 
347  std::unique_ptr<cbg_executor::TimerManager> timer_manager;
348 
351  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> global_executable_cache;
352 
354  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> nodes_executable_cache;
355 };
356 
357 } // namespace executors
358 } // namespace rclcpp
Coordinate the order and timing of available communication tasks.
Definition: executor.hpp:61
static RCLCPP_PUBLIC void execute_timer(const rclcpp::TimerBase::SharedPtr &timer, const std::shared_ptr< void > &data_ptr)
Run timer executable.
Definition: executor.cpp:682
static RCLCPP_PUBLIC void execute_client(const rclcpp::ClientBase::SharedPtr &client)
Run service client executable.
Definition: executor.cpp:702
static RCLCPP_PUBLIC void execute_service(const rclcpp::ServiceBase::SharedPtr &service)
Run service server executable.
Definition: executor.cpp:690
std::atomic_bool cancel_requested_
Tracks a pending cancel request that has not yet been consumed by a spin.
Definition: executor.hpp:575
static RCLCPP_PUBLIC void execute_subscription(const rclcpp::SubscriptionBase::SharedPtr &subscription)
Run subscription executable.
Definition: executor.cpp:579
Node is the single point of entry for creating publishers and subscribers.
Definition: node.hpp:78
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_all_callback_groups() override
Get callback groups that belong to executor.
RCLCPP_PUBLIC void spin_once(std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1)) override
Collect work once and execute the next available work, optionally within a duration.
RCLCPP_PUBLIC void spin(const std::function< void(const std::exception &)> &exception_handler)
RCLCPP_PUBLIC void add_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr, const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true) override
Add a callback group to an executor.
RCLCPP_PUBLIC void spin_some(std::chrono::nanoseconds max_duration=std::chrono::nanoseconds(0)) override
Collect work once and execute all available work, optionally within a max duration.
RCLCPP_PUBLIC void add_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true) override
Add a node to the executor.
bool execute_previous_ready_executables_until(const std::chrono::time_point< std::chrono::steady_clock > &stop_time)
RCLCPP_PUBLIC EventsCBGExecutor(const rclcpp::ExecutorOptions &options=rclcpp::ExecutorOptions(), size_t number_of_threads=0, std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1))
RCLCPP_PUBLIC void spin_all(std::chrono::nanoseconds max_duration) override
Collect and execute work repeatedly within a duration or until no more work is available.
RCLCPP_PUBLIC void remove_node(const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr &node_ptr, bool notify=true) override
Remove a node from the executor.
RCLCPP_PUBLIC bool collect_and_execute_ready_events(std::chrono::nanoseconds max_duration, bool recollect_if_no_work_available)
RCLCPP_PUBLIC void cancel() override
Cancel any running spin* function, causing it to return.
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_manually_added_callback_groups() override
Get callback groups that belong to executor.
RCLCPP_PUBLIC std::vector< rclcpp::CallbackGroup::WeakPtr > get_automatically_added_callback_groups_from_nodes() override
Get callback groups that belong to executor.
RCLCPP_PUBLIC void spin() override
RCLCPP_PUBLIC void remove_callback_group(const rclcpp::CallbackGroup::SharedPtr &group_ptr, bool notify=true) override
Remove a callback group from the executor.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC bool ok(const rclcpp::Context::SharedPtr &context=rclcpp::contexts::get_global_default_context())
Check rclcpp's status.
FutureReturnCode
Return codes to be used with spin_until_future_complete.
Options to be passed to the executor constructor.