ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
event_handler.hpp
1 // Copyright 2019 Open Source Robotics Foundation, Inc.
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 #ifndef RCLCPP__EVENT_HANDLER_HPP_
16 #define RCLCPP__EVENT_HANDLER_HPP_
17 
18 #include <atomic>
19 #include <functional>
20 #include <memory>
21 #include <mutex>
22 #include <stdexcept>
23 #include <string>
24 #include <vector>
25 
26 #include "rcl/error_handling.h"
27 #include "rcl/event_callback.h"
28 #include "rmw/impl/cpp/demangle.hpp"
29 #include "rmw/incompatible_qos_events_statuses.h"
30 #include "rmw/events_statuses/incompatible_type.h"
31 
32 #include "rcutils/logging_macros.h"
33 
34 #include "rclcpp/detail/cpp_callback_trampoline.hpp"
35 #include "rclcpp/exceptions.hpp"
36 #include "rclcpp/function_traits.hpp"
37 #include "rclcpp/logging.hpp"
38 #include "rclcpp/waitable.hpp"
39 
40 namespace rclcpp
41 {
42 
43 using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
44 using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
45 using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
46 using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
47 using QOSMessageLostInfo = rmw_message_lost_status_t;
48 using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
49 using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;
50 
51 using IncompatibleTypeInfo = rmw_incompatible_type_status_t;
52 using MatchedInfo = rmw_matched_status_t;
53 
54 using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
55 using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
56 using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
57 using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
58 using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
59 using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
60 using QOSRequestedIncompatibleQoSCallbackType =
61  std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
62 
63 using IncompatibleTypeCallbackType = std::function<void (IncompatibleTypeInfo &)>;
64 using PublisherMatchedCallbackType = std::function<void (MatchedInfo &)>;
65 using SubscriptionMatchedCallbackType = std::function<void (MatchedInfo &)>;
66 
69 {
70  QOSDeadlineOfferedCallbackType deadline_callback;
71  QOSLivelinessLostCallbackType liveliness_callback;
72  QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
73  IncompatibleTypeCallbackType incompatible_type_callback;
74  PublisherMatchedCallbackType matched_callback;
75 };
76 
79 {
80  QOSDeadlineRequestedCallbackType deadline_callback;
81  QOSLivelinessChangedCallbackType liveliness_callback;
82  QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
83  QOSMessageLostCallbackType message_lost_callback;
84  IncompatibleTypeCallbackType incompatible_type_callback;
85  SubscriptionMatchedCallbackType matched_callback;
86 };
87 
88 class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
89 {
90 public:
91  RCLCPP_PUBLIC
93  rcl_ret_t ret,
94  const rcl_error_state_t * error_state,
95  const std::string & prefix);
96 
97  RCLCPP_PUBLIC
99  const exceptions::RCLErrorBase & base_exc,
100  const std::string & prefix);
101 };
102 
104 {
105 public:
106  enum class EntityType : std::size_t
107  {
108  Event,
109  };
110 
111  RCLCPP_PUBLIC
112  virtual ~EventHandlerBase();
113 
114  RCLCPP_PUBLIC
115  virtual
116  void enable() = 0;
117 
118  RCLCPP_PUBLIC
119  virtual
120  void disable() = 0;
121 
123  RCLCPP_PUBLIC
124  size_t
125  get_number_of_ready_events() override;
126 
128  RCLCPP_PUBLIC
129  void
130  add_to_wait_set(rcl_wait_set_t & wait_set) override;
131 
133  RCLCPP_PUBLIC
134  bool
135  is_ready(const rcl_wait_set_t & wait_set) override;
136 
138 
173  void
174  set_on_ready_callback(std::function<void(size_t, int)> callback) override
175  {
176  if (!callback) {
177  throw std::invalid_argument(
178  "The callback passed to set_on_ready_callback "
179  "is not callable.");
180  }
181 
182  // Note: we bind the int identifier argument to this waitable's entity types
183  auto new_callback =
184  [callback, this](size_t number_of_events) {
185  try {
186  callback(number_of_events, static_cast<int>(EntityType::Event));
187  } catch (const std::exception & exception) {
188  RCLCPP_ERROR_STREAM(
189  // TODO(wjwwood): get this class access to the node logger it is associated with
190  rclcpp::get_logger("rclcpp"),
191  "rclcpp::EventHandlerBase@" << this <<
192  " caught " << rmw::impl::cpp::demangle(exception) <<
193  " exception in user-provided callback for the 'on ready' callback: " <<
194  exception.what());
195  } catch (...) {
196  RCLCPP_ERROR_STREAM(
197  rclcpp::get_logger("rclcpp"),
198  "rclcpp::EventHandlerBase@" << this <<
199  " caught unhandled exception in user-provided callback " <<
200  "for the 'on ready' callback");
201  }
202  };
203 
204  std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
205 
206  // Set it temporarily to the new callback, while we replace the old one.
207  // This two-step setting, prevents a gap where the old std::function has
208  // been replaced but the middleware hasn't been told about the new one yet.
209  set_on_new_event_callback(
210  rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
211  static_cast<const void *>(&new_callback));
212 
213  // Store the std::function to keep it in scope, also overwrites the existing one.
214  on_new_event_callback_ = new_callback;
215 
216  // Set it again, now using the permanent storage.
217  set_on_new_event_callback(
218  rclcpp::detail::cpp_callback_trampoline<
219  decltype(on_new_event_callback_), const void *, size_t>,
220  static_cast<const void *>(&on_new_event_callback_));
221  }
222 
224  void
226  {
227  std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
228  if (on_new_event_callback_) {
229  set_on_new_event_callback(nullptr, nullptr);
230  on_new_event_callback_ = nullptr;
231  }
232  }
233 
234  RCLCPP_PUBLIC
235  std::vector<std::shared_ptr<rclcpp::TimerBase>>
236  get_timers() const override
237  {
238  return {};
239  }
240 
241 protected:
242  RCLCPP_PUBLIC
243  void
244  set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
245 
246  std::recursive_mutex on_new_event_callback_mutex_;
247  std::function<void(size_t)> on_new_event_callback_{nullptr};
248 
249  rcl_event_t event_handle_;
250  size_t wait_set_event_index_;
251 };
252 
253 template<typename EventCallbackT, typename ParentHandleT>
255 {
256 public:
257  template<typename InitFuncT, typename EventTypeEnum>
258  EventHandler(
259  const EventCallbackT & callback,
260  InitFuncT init_func,
261  ParentHandleT parent_handle,
262  EventTypeEnum event_type)
263  : parent_handle_(parent_handle), event_callback_(callback)
264  {
265  event_handle_ = rcl_get_zero_initialized_event();
266  rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
267  if (ret != RCL_RET_OK) {
268  if (ret == RCL_RET_UNSUPPORTED) {
269  UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
270  rcl_reset_error();
271  throw exc;
272  } else {
273  rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
274  }
275  }
276  }
277 
278  ~EventHandler()
279  {
280  // Since the rmw event listener holds a reference to the
281  // "on ready" callback, we need to clear it on destruction of this class.
282  // This clearing is not needed for other rclcpp entities like pub/subs, since
283  // they do own the underlying rmw entities, which are destroyed
284  // on their rclcpp destructors, thus no risk of dangling pointers.
286  }
287 
289  std::shared_ptr<void>
290  take_data() override
291  {
292  EventCallbackInfoT callback_info;
293  rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
294  if (ret != RCL_RET_OK) {
295  RCUTILS_LOG_ERROR_NAMED(
296  "rclcpp",
297  "Couldn't take event info: %s", rcl_get_error_string().str);
298  rcl_reset_error();
299  return nullptr;
300  }
301  return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
302  }
303 
304  std::shared_ptr<void>
305  take_data_by_entity_id([[maybe_unused]] size_t id) override
306  {
307  return take_data();
308  }
309 
311  void
312  execute(const std::shared_ptr<void> & data) override
313  {
314  std::unique_lock<std::mutex> event_callback_lock(event_callback_mutex_);
315  if (disabled_.load()) {
316  return;
317  }
318  if (!data) {
319  throw std::runtime_error("'data' is empty");
320  }
321  auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
322  event_callback_(*callback_ptr);
323  callback_ptr.reset();
324  }
325 
327 
331  void disable() override
332  {
333  {
334  // Temporary remove the on_new_event_callback_ to prevent it from being called
335  std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
336  if (on_new_event_callback_) {
337  set_on_new_event_callback(nullptr, nullptr);
338  }
339  }
340  std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
341  disabled_.store(true);
342  }
343 
345 
349  void enable() override
350  {
351  {
352  // Set callback again if it was previously removed in disable()
353  std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
354  if (on_new_event_callback_) {
355  set_on_new_event_callback(
356  rclcpp::detail::cpp_callback_trampoline<
357  decltype(on_new_event_callback_), const void *, size_t>,
358  static_cast<const void *>(&on_new_event_callback_));
359  }
360  }
361  std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
362  disabled_.store(false);
363  }
364 
365 private:
366  using EventCallbackInfoT = typename std::remove_reference<typename
368 
369  ParentHandleT parent_handle_;
370  EventCallbackT event_callback_;
371  std::mutex event_callback_mutex_;
372  std::atomic_bool disabled_{false};
373 };
374 } // namespace rclcpp
375 
376 #endif // RCLCPP__EVENT_HANDLER_HPP_
RCLCPP_PUBLIC size_t get_number_of_ready_events() override
Get the number of ready events.
RCLCPP_PUBLIC void add_to_wait_set(rcl_wait_set_t &wait_set) override
Add the Waitable to a wait set.
void set_on_ready_callback(std::function< void(size_t, int)> callback) override
Set a callback to be called when each new event instance occurs.
RCLCPP_PUBLIC std::vector< std::shared_ptr< rclcpp::TimerBase > > get_timers() const override
Returns all timers used by this waitable.
RCLCPP_PUBLIC bool is_ready(const rcl_wait_set_t &wait_set) override
Check if the Waitable is ready.
void clear_on_ready_callback() override
Unset the callback registered for new events, if any.
void disable() override
Disable the event callback from being called when execute(..) invoked.
void enable() override
Enable the event callback to be called when execute(..) invoked.
void execute(const std::shared_ptr< void > &data) override
Execute any entities of the Waitable that are ready.
std::shared_ptr< void > take_data() override
Take data so that the callback cannot be scheduled again.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_take_event(const rcl_event_t *event, void *event_info)
Definition: event.c:166
RCL_PUBLIC RCL_WARN_UNUSED rcl_event_t rcl_get_zero_initialized_event(void)
Return a rcl_event_t struct with members set to NULL.
Definition: event.c:39
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
Structure which encapsulates a ROS QoS event handle.
Definition: event.h:61
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:42
Contains callbacks for various types of events a Publisher can receive from the middleware.
Contains callbacks for non-message events that a Subscription can receive from the middleware.
#define RCL_RET_UNSUPPORTED
Unsupported return code.
Definition: types.h:37
#define RCL_RET_OK
Success return code.
Definition: types.h:27
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24