ROS 2 rclcpp + rcl - jazzy  jazzy
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  rclcpp::CallbackGroup::SharedPtr group_ptr,
70  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  rclcpp::CallbackGroup::SharedPtr group_ptr,
89  bool notify = true) override;
90 
91  RCLCPP_PUBLIC
92  void
93  add_node(
94  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
95  bool notify = true) override;
96 
98 
101  RCLCPP_PUBLIC
102  void
103  add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
104 
105  RCLCPP_PUBLIC
106  void
107  remove_node(
108  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
109  bool notify = true) override;
110 
112 
115  RCLCPP_PUBLIC
116  void
117  remove_node(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(this->spinning.store(false); );
209  while (rclcpp::ok(this->context_) && spinning.load()) {
210  // Do one item of work.
211  spin_once_internal(timeout_left);
212 
213  // Check if the future is set, return SUCCESS if it is.
214  status = future.wait_for(std::chrono::seconds(0));
215  if (status == std::future_status::ready) {
216  return FutureReturnCode::SUCCESS;
217  }
218  // If the original timeout is < 0, then this is blocking, never TIMEOUT.
219  if (timeout_ns < std::chrono::nanoseconds::zero()) {
220  continue;
221  }
222  // Otherwise check if we still have time to wait, return TIMEOUT if not.
223  auto now = std::chrono::steady_clock::now();
224  if (now >= end_time) {
225  return FutureReturnCode::TIMEOUT;
226  }
227  // Subtract the elapsed time from the original timeout.
228  timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
229  }
230 
231  // The future did not complete before ok() returned false, return INTERRUPTED.
232  return FutureReturnCode::INTERRUPTED;
233  }
234 
240 
241 protected:
242  RCLCPP_PUBLIC
243  void
244  run(size_t this_thread_number, bool block_initially);
245 
246  RCLCPP_PUBLIC
247  void
248  run(
249  size_t this_thread_number,
250  const std::function<void(const std::exception &)> & exception_handler);
251 
256  void shutdown();
257 
258  std::unique_ptr<cbg_executor::CBGScheduler> scheduler;
259 
261  {
262  enum class Origin
263  {
264  Node,
265  ManualAdded,
266  };
267 
268  CallbackGroup::WeakPtr callback_group;
269 
270  std::unique_ptr<cbg_executor::RegisteredEntityCache> registered_entities;
271 
272  Origin origin;
273  };
274 
275  void set_callbacks(CallbackGroupData & cgd);
276 
282  const std::chrono::time_point<std::chrono::steady_clock> & stop_time);
283 
284  void unregister_event_callbacks(const rclcpp::CallbackGroup::SharedPtr & cbg) const;
285 
286 private:
287  void remove_all_nodes_and_callback_groups();
288 
289  void sync_callback_groups();
290 
295  void trigger_callback_group_sync();
296 
297  RCLCPP_PUBLIC
298  void spin_once_internal(std::chrono::nanoseconds timeout);
299 
300  RCLCPP_DISABLE_COPY(EventsCBGExecutor)
301 
302  std::mutex added_callback_groups_mutex_;
303  std::vector<rclcpp::CallbackGroup::WeakPtr> added_callback_groups;
304 
305  std::mutex added_nodes_mutex_;
306  std::vector<node_interfaces::NodeBaseInterface::WeakPtr> added_nodes;
307 
308  std::mutex callback_groups_mutex;
309 
310  std::vector<CallbackGroupData> callback_groups;
311 
312  size_t number_of_threads_;
313 
314  std::chrono::nanoseconds next_exec_timeout_;
315 
316  std::atomic_bool needs_callback_group_resync = false;
317 
319  std::atomic_bool spinning;
320 
322  bool in_shutdown = false;
323 
325  std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
326 
328  std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
329 
331  rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
332 
334  std::shared_ptr<rclcpp::Context> context_;
335 
336  std::unique_ptr<cbg_executor::TimerManager> timer_manager;
337 
340  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> global_executable_cache;
341 
343  std::unique_ptr<cbg_executor::GlobalWeakExecutableCache> nodes_executable_cache;
344 };
345 
346 } // namespace executors
347 } // namespace rclcpp
Coordinate the order and timing of available communication tasks.
Definition: executor.hpp:65
static RCLCPP_PUBLIC void execute_service(rclcpp::ServiceBase::SharedPtr service)
Run service server executable.
Definition: executor.cpp:650
static RCLCPP_PUBLIC void execute_timer(rclcpp::TimerBase::SharedPtr timer, const std::shared_ptr< void > &data_ptr)
Run timer executable.
Definition: executor.cpp:644
static RCLCPP_PUBLIC void execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
Run subscription executable.
Definition: executor.cpp:543
static RCLCPP_PUBLIC void execute_client(rclcpp::ClientBase::SharedPtr client)
Run service client executable.
Definition: executor.cpp:662
Node is the single point of entry for creating publishers and subscribers.
Definition: node.hpp:80
RCLCPP_PUBLIC void remove_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr, bool notify=true) override
Remove a callback group from the executor.
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 add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify=true) override
Add a node to the executor.
RCLCPP_PUBLIC void spin(const std::function< void(const std::exception &)> &exception_handler)
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.
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 add_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr, rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify=true) override
Add a callback group to an executor.
RCLCPP_PUBLIC void remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify=true) override
Remove a node from the executor.
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 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
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
FutureReturnCode
Return codes to be used with spin_until_future_complete.
RCLCPP_PUBLIC bool ok(rclcpp::Context::SharedPtr context=nullptr)
Check rclcpp's status.
Options to be passed to the executor constructor.