ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
subscription_intra_process.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__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
16 #define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
17 
18 #include <rmw/types.h>
19 
20 #include <chrono>
21 #include <functional>
22 #include <memory>
23 #include <stdexcept>
24 #include <string>
25 #include <type_traits>
26 #include <utility>
27 
28 #include "rcl/types.h"
29 
30 #include "rclcpp/any_subscription_callback.hpp"
31 #include "rclcpp/context.hpp"
32 #include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
33 #include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
34 #include "rclcpp/logging.hpp"
35 #include "rclcpp/qos.hpp"
36 #include "rclcpp/time.hpp"
37 #include "rclcpp/type_support_decl.hpp"
38 #include "tracetools/tracetools.h"
39 
40 namespace rclcpp
41 {
42 namespace experimental
43 {
44 
45 template<
46  typename MessageT,
47  typename SubscribedType,
48  typename SubscribedTypeAlloc = std::allocator<SubscribedType>,
49  typename SubscribedTypeDeleter = std::default_delete<SubscribedType>,
50  typename ROSMessageType = SubscribedType,
51  typename Alloc = std::allocator<void>
52 >
55  SubscribedType,
56  SubscribedTypeAlloc,
57  SubscribedTypeDeleter,
58  ROSMessageType
59  >
60 {
62  SubscribedType,
63  SubscribedTypeAlloc,
64  SubscribedTypeDeleter,
65  ROSMessageType
66  >;
67 
68 public:
69  RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
70 
71  using MessageAllocTraits =
72  typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocatorTraits;
73  using MessageAlloc = typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocator;
74  using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstDataSharedPtr;
75  using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::SubscribedTypeUniquePtr;
76  using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
77  using StatsHandlerFn = std::function<void(const rmw_message_info_t &, const rclcpp::Time &)>;
78 
81  std::shared_ptr<Alloc> allocator,
82  rclcpp::Context::SharedPtr context,
83  const std::string & topic_name,
84  const rclcpp::QoS & qos_profile,
86  StatsHandlerFn stats_handler = nullptr)
87  : SubscriptionIntraProcessBuffer<SubscribedType, SubscribedTypeAlloc,
88  SubscribedTypeDeleter, ROSMessageType>(
89  std::make_shared<SubscribedTypeAlloc>(*allocator),
90  context,
91  topic_name,
92  qos_profile,
93  buffer_type),
94  any_callback_(callback),
95  stats_handler_(std::move(stats_handler))
96  {
97  TRACETOOLS_TRACEPOINT(
98  rclcpp_subscription_callback_added,
99  static_cast<const void *>(this),
100  static_cast<const void *>(&any_callback_));
101  // The callback object gets copied, so if registration is done too early/before this point
102  // (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
103  // in subsequent tracepoints.
104 #ifndef TRACETOOLS_DISABLED
105  any_callback_.register_callback_for_tracing();
106 #endif
107  }
108 
109  virtual ~SubscriptionIntraProcess() = default;
110 
111  void
112  add_to_wait_set(rcl_wait_set_t & wait_set) override
113  {
114  // This block is necessary when the guard condition wakes the wait set, but
115  // the intra process waitable was not handled before the wait set is waited
116  // on again.
117  // Basically we're keeping the guard condition triggered so long as there is
118  // data in the buffer.
119  if (this->buffer_->has_data()) {
120  // If there is data still to be processed, indicate to the
121  // executor or waitset by triggering the guard condition.
122  this->trigger_guard_condition();
123  }
124  // Let the parent classes handle the rest of the work:
126  }
127 
128  std::shared_ptr<void>
129  take_data() override
130  {
131  ConstMessageSharedPtr shared_msg;
132  MessageUniquePtr unique_msg;
133 
134  if (any_callback_.use_take_shared_method()) {
135  shared_msg = this->buffer_->consume_shared();
136  if (!shared_msg) {
137  return nullptr;
138  }
139  } else {
140  unique_msg = this->buffer_->consume_unique();
141  if (!unique_msg) {
142  return nullptr;
143  }
144  }
145 
146  if (this->buffer_->has_data()) {
147  // If there is data still to be processed, indicate to the
148  // executor or waitset by triggering the guard condition.
149  this->trigger_guard_condition();
150  }
151 
152  return std::static_pointer_cast<void>(
153  std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
154  std::pair<ConstMessageSharedPtr, MessageUniquePtr>(
155  shared_msg, std::move(unique_msg)))
156  );
157  }
158 
159  void execute(const std::shared_ptr<void> & data) override
160  {
161  execute_impl<SubscribedType>(data);
162  }
163 
165 
170  void disable_callbacks() override
171  {
173  any_callback_.disable();
174  }
175 
177 
181  void enable_callbacks() override
182  {
184  any_callback_.enable();
185  }
186 
187 protected:
188  template<typename T>
189  typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
190  execute_impl(const std::shared_ptr<void> &)
191  {
192  throw std::runtime_error("Subscription intra-process can't handle serialized messages");
193  }
194 
195  template<class T>
196  typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
197  execute_impl(const std::shared_ptr<void> & data)
198  {
199  if (nullptr == data) {
200  return;
201  }
202 
203  rmw_message_info_t msg_info;
204  msg_info.publisher_gid = {0, {0}};
205  msg_info.from_intra_process = true;
206 
207  const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(
208  std::chrono::system_clock::now());
209  if (stats_handler_) {
210  RCLCPP_WARN_ONCE(
211  rclcpp::get_logger("rclcpp"),
212  "Intra-process communication does not support accurate message age statistics");
213  // Set source_timestamp to "now" so that message_age reports 0ms rather than
214  // an invalid value taken from an un-initialised timestamp. IPC delivery
215  // has little/no transport latency by definition, so near-zero age is expected.
216  msg_info.source_timestamp = nanos.time_since_epoch().count();
217  }
218 
219  auto shared_ptr = std::static_pointer_cast<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
220  data);
221 
222  if (any_callback_.use_take_shared_method()) {
223  ConstMessageSharedPtr shared_msg = shared_ptr->first;
224  any_callback_.dispatch_intra_process(shared_msg, msg_info);
225  } else {
226  MessageUniquePtr unique_msg = std::move(shared_ptr->second);
227  any_callback_.dispatch_intra_process(std::move(unique_msg), msg_info);
228  }
229  shared_ptr.reset();
230 
231  if (stats_handler_) {
232  stats_handler_(msg_info, rclcpp::Time(nanos.time_since_epoch().count()));
233  }
234  }
235 
236  AnySubscriptionCallback<MessageT, Alloc> any_callback_;
237  StatsHandlerFn stats_handler_;
238 };
239 
240 } // namespace experimental
241 } // namespace rclcpp
242 
243 #endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
virtual RCLCPP_PUBLIC void enable_callbacks()
Enable the callbacks to be called.
virtual RCLCPP_PUBLIC void disable_callbacks()
Disable callbacks from being called.
void add_to_wait_set(rcl_wait_set_t &wait_set) override
Add the Waitable to a wait set.
void disable_callbacks() override
Disable callbacks from being called.
std::shared_ptr< void > take_data() override
Take the data so that it can be consumed with execute.
void enable_callbacks() override
Enable the callbacks to be called.
void add_to_wait_set(rcl_wait_set_t &wait_set) override
Add the Waitable to a wait set.
void execute(const std::shared_ptr< void > &data) override
Execute data that is passed in.
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
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:42