ROS 2 rclcpp + rcl - rolling  rolling-20536064
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/visibility_control.hpp"
26 
27 namespace rclcpp
28 {
29 namespace executors
30 {
31 
32 namespace cbg_executor
33 {
34 class TimerManager;
35 struct RegisteredEntityCache;
36 class CBGScheduler;
37 struct GlobalWeakExecutableCache;
38 }
39 
41 {
42 public:
43  RCLCPP_SMART_PTR_DEFINITIONS(EventsCBGExecutor)
44 
45 
57  RCLCPP_PUBLIC
58  explicit EventsCBGExecutor(
60  size_t number_of_threads = 0,
61  std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
62 
63  RCLCPP_PUBLIC
64  virtual ~EventsCBGExecutor();
65 
66  RCLCPP_PUBLIC
67  void
69  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
70  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
71  bool notify = true) override;
72 
73  RCLCPP_PUBLIC
74  std::vector<rclcpp::CallbackGroup::WeakPtr>
75  get_all_callback_groups() override;
76 
77  RCLCPP_PUBLIC
78  std::vector<rclcpp::CallbackGroup::WeakPtr>
80 
81  RCLCPP_PUBLIC
82  std::vector<rclcpp::CallbackGroup::WeakPtr>
84 
85  RCLCPP_PUBLIC
86  void
88  const rclcpp::CallbackGroup::SharedPtr & group_ptr,
89  bool notify = true) override;
90 
91  RCLCPP_PUBLIC
92  void
93  add_node(
94  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
95  bool notify = true) override;
96 
98 
101  RCLCPP_PUBLIC
102  void
103  add_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify = true) override;
104 
105  RCLCPP_PUBLIC
106  void
107  remove_node(
108  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node_ptr,
109  bool notify = true) override;
110 
112 
115  RCLCPP_PUBLIC
116  void
117  remove_node(const std::shared_ptr<rclcpp::Node> & node_ptr, bool notify = true) override;
118 
119  // add a callback group to the executor, not bound to any node
120  void add_callback_group_only(const rclcpp::CallbackGroup::SharedPtr & group_ptr);
121 
126  RCLCPP_PUBLIC
127  void
128  spin() override;
129 
138  RCLCPP_PUBLIC
139  void
140  spin(const std::function<void(const std::exception &)> & exception_handler);
141 
142  RCLCPP_PUBLIC
143  void
144  spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)) override;
145 
146  RCLCPP_PUBLIC
147  void
148  spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
149 
153  RCLCPP_PUBLIC
155  std::chrono::nanoseconds max_duration,
156  bool recollect_if_no_work_available);
157 
158  RCLCPP_PUBLIC
159  void
160  spin_all(std::chrono::nanoseconds max_duration) override;
161 
163 
167  RCLCPP_PUBLIC
168  void
169  cancel() override;
170 
171  RCLCPP_PUBLIC
172  size_t
173  get_number_of_threads() const;
174 
175  bool
176  is_spinning()
177  {
178  return spinning;
179  }
180 
181  template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
183  spin_until_future_complete(
184  const FutureT & future,
185  std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
186  {
187  // TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
188  // inside a callback executed by an executor.
189 
190  // Check the future before entering the while loop.
191  // If the future is already complete, don't try to spin.
192  std::future_status status = future.wait_for(std::chrono::seconds(0));
193  if (status == std::future_status::ready) {
194  return FutureReturnCode::SUCCESS;
195  }
196 
197  auto end_time = std::chrono::steady_clock::now();
198  std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
199  timeout);
200  if (timeout_ns > std::chrono::nanoseconds::zero()) {
201  end_time += timeout_ns;
202  }
203  std::chrono::nanoseconds timeout_left = timeout_ns;
204 
205  if (spinning.exchange(true)) {
206  throw std::runtime_error("spin_until_future_complete() called while already spinning");
207  }
208  RCPPUTILS_SCOPE_EXIT(
209  this->spinning.store(false);
210  this->cancel_requested_.store(false); );
211  if (cancel_requested_.load()) {
212  return FutureReturnCode::INTERRUPTED;
213  }
214  while (rclcpp::ok(this->context_) && !cancel_requested_.load()) {
215  // Do one item of work.
216  spin_once_internal(timeout_left);
217 
218  // Check if the future is set, return SUCCESS if it is.
219  status = future.wait_for(std::chrono::seconds(0));
220  if (status == std::future_status::ready) {
221  return FutureReturnCode::SUCCESS;
222  }
223  // If the original timeout is < 0, then this is blocking, never TIMEOUT.
224  if (timeout_ns < std::chrono::nanoseconds::zero()) {
225  continue;
226  }
227  // Otherwise check if we still have time to wait, return TIMEOUT if not.
228  auto now = std::chrono::steady_clock::now();
229  if (now >= end_time) {
230  return FutureReturnCode::TIMEOUT;
231  }
232  // Subtract the elapsed time from the original timeout.
233  timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
234  }
235 
236  // The future did not complete before ok() returned false, return INTERRUPTED.
237  return FutureReturnCode::INTERRUPTED;
238  }
239 
245 
246 protected:
247  RCLCPP_PUBLIC
248  void
249  run(size_t this_thread_number, bool block_initially);
250 
251  RCLCPP_PUBLIC
252  void
253  run(
254  size_t this_thread_number,
255  const std::function<void(const std::exception &)> & exception_handler);
256 
261  void shutdown();
262 
263  std::unique_ptr<cbg_executor::CBGScheduler> scheduler;
264 
266  {
267  enum class Origin
268  {
269  Node,
270  ManualAdded,
271  };
272 
273  CallbackGroup::WeakPtr callback_group;
274 
275  std::unique_ptr<cbg_executor::RegisteredEntityCache> registered_entities;
276 
277  Origin origin;
278  };
279 
280  void set_callbacks(CallbackGroupData & cgd);
281 
287  const std::chrono::time_point<std::chrono::steady_clock> & stop_time);
288 
289  void unregister_event_callbacks(const rclcpp::CallbackGroup::SharedPtr & cbg) const;
290 
291 private:
292  void remove_all_nodes_and_callback_groups();
293 
294  void sync_callback_groups();
295 
300  void trigger_callback_group_sync();
301 
302  RCLCPP_PUBLIC
303  void spin_once_internal(std::chrono::nanoseconds timeout);
304 
305  RCLCPP_DISABLE_COPY(EventsCBGExecutor)
306 
307  std::mutex added_callback_groups_mutex_;
308  std::vector<rclcpp::CallbackGroup::WeakPtr> added_callback_groups;
309 
310  std::mutex added_nodes_mutex_;
311  std::vector<node_interfaces::NodeBaseInterface::WeakPtr> added_nodes;
312 
313  std::mutex callback_groups_mutex;
314 
315  std::vector<CallbackGroupData> callback_groups;
316 
317  size_t number_of_threads_;
318 
319  std::chrono::nanoseconds next_exec_timeout_;
320 
321  std::atomic_bool needs_callback_group_resync = false;
322 
324 
329  std::atomic_bool spinning;
330 
332  bool in_shutdown = false;
333 
335  std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
336 
338  std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
339 
341  rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
342 
344  std::shared_ptr<rclcpp::Context> context_;
345 
346  std::unique_ptr<cbg_executor::TimerManager> timer_manager;
347 
350  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> global_executable_cache;
351 
353  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> nodes_executable_cache;
354 };
355 
356 } // namespace executors
357 } // namespace rclcpp
Coordinate the order and timing of available communication tasks.
Definition: executor.hpp:64
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:578
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:79
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.