ROS 2 rclcpp + rcl - rolling  rolling-29de98cf
ROS 2 C++ Client Library with ROS Client Library
wait_result.hpp
1 // Copyright 2020 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__WAIT_RESULT_HPP_
16 #define RCLCPP__WAIT_RESULT_HPP_
17 
18 #include <cassert>
19 #include <functional>
20 #include <memory>
21 #include <stdexcept>
22 #include <utility>
23 
24 #include "rcl/wait.h"
25 
26 #include "rclcpp/macros.hpp"
27 #include "rclcpp/wait_result_kind.hpp"
28 
29 #include "rclcpp/client.hpp"
30 #include "rclcpp/service.hpp"
31 #include "rclcpp/subscription_base.hpp"
32 #include "rclcpp/timer.hpp"
33 
34 namespace rclcpp
35 {
36 
37 // TODO(wjwwood): the union-like design of this class could be replaced with
38 // std::variant, when we have access to that...
40 
60 template<class WaitSetT>
61 class WaitResult final
62 {
63 public:
65 
70  static
72  from_ready_wait_result_kind(WaitSetT & wait_set)
73  {
74  return WaitResult(WaitResultKind::Ready, wait_set);
75  }
76 
78  static
81  {
82  return WaitResult(WaitResultKind::Timeout);
83  }
84 
86  static
89  {
90  return WaitResult(WaitResultKind::Empty);
91  }
92 
94  WaitResultKind
95  kind() const
96  {
97  return wait_result_kind_;
98  }
99 
101 
105  const WaitSetT &
106  get_wait_set() const
107  {
108  if (this->kind() != WaitResultKind::Ready) {
109  throw std::runtime_error("cannot access wait set when the result was not ready");
110  }
111  // This should never happen, defensive (and debug mode) check only.
112  assert(wait_set_pointer_);
113  return *wait_set_pointer_;
114  }
115 
117 
121  WaitSetT &
123  {
124  if (this->kind() != WaitResultKind::Ready) {
125  throw std::runtime_error("cannot access wait set when the result was not ready");
126  }
127  // This should never happen, defensive (and debug mode) check only.
128  assert(wait_set_pointer_);
129  return *wait_set_pointer_;
130  }
131 
132  WaitResult(WaitResult && other) noexcept
133  : wait_result_kind_(other.wait_result_kind_),
134  wait_set_pointer_(std::exchange(other.wait_set_pointer_, nullptr))
135  {}
136 
137  ~WaitResult()
138  {
139  if (wait_set_pointer_) {
140  wait_set_pointer_->wait_result_release();
141  }
142  }
143 
145 
166  std::pair<std::shared_ptr<rclcpp::TimerBase>, size_t>
167  peek_next_ready_timer(size_t start_index = 0)
168  {
169  check_wait_result_dirty();
170  auto ret = std::shared_ptr<rclcpp::TimerBase>{nullptr};
171  size_t ii = start_index;
172  if (this->kind() == WaitResultKind::Ready) {
173  auto & wait_set = this->get_wait_set();
174  auto & rcl_wait_set = wait_set.storage_get_rcl_wait_set();
175  for (; ii < wait_set.size_of_timers(); ++ii) {
176  if (rcl_wait_set.timers[ii] != nullptr) {
177  ret = wait_set.timers(ii);
178  if (ret) {
179  break;
180  }
181  }
182  }
183  }
184  return {ret, ii};
185  }
186 
188 
197  void
199  {
200  auto & wait_set = this->get_wait_set();
201  auto & rcl_wait_set = wait_set.storage_get_rcl_wait_set();
202  if (index >= wait_set.size_of_timers()) {
203  throw std::out_of_range("given timer index is out of range");
204  }
205  rcl_wait_set.timers[index] = nullptr;
206  }
207 
209  std::shared_ptr<rclcpp::SubscriptionBase>
211  {
212  check_wait_result_dirty();
213  auto ret = std::shared_ptr<rclcpp::SubscriptionBase>{nullptr};
214  if (this->kind() == WaitResultKind::Ready) {
215  auto & wait_set = this->get_wait_set();
216  auto & rcl_wait_set = wait_set.storage_get_rcl_wait_set();
217  for (size_t ii = 0; ii < wait_set.size_of_subscriptions(); ++ii) {
218  if (rcl_wait_set.subscriptions[ii] != nullptr) {
219  ret = wait_set.subscriptions(ii);
220  rcl_wait_set.subscriptions[ii] = nullptr;
221  if (ret) {
222  break;
223  }
224  }
225  }
226  }
227  return ret;
228  }
229 
231  std::shared_ptr<rclcpp::ServiceBase>
233  {
234  check_wait_result_dirty();
235  auto ret = std::shared_ptr<rclcpp::ServiceBase>{nullptr};
236  if (this->kind() == WaitResultKind::Ready) {
237  auto & wait_set = this->get_wait_set();
238  auto & rcl_wait_set = wait_set.storage_get_rcl_wait_set();
239  for (size_t ii = 0; ii < wait_set.size_of_services(); ++ii) {
240  if (rcl_wait_set.services[ii] != nullptr) {
241  ret = wait_set.services(ii);
242  rcl_wait_set.services[ii] = nullptr;
243  if (ret) {
244  break;
245  }
246  }
247  }
248  }
249  return ret;
250  }
251 
253  std::shared_ptr<rclcpp::ClientBase>
255  {
256  check_wait_result_dirty();
257  auto ret = std::shared_ptr<rclcpp::ClientBase>{nullptr};
258  if (this->kind() == WaitResultKind::Ready) {
259  auto & wait_set = this->get_wait_set();
260  auto & rcl_wait_set = wait_set.storage_get_rcl_wait_set();
261  for (size_t ii = 0; ii < wait_set.size_of_clients(); ++ii) {
262  if (rcl_wait_set.clients[ii] != nullptr) {
263  ret = wait_set.clients(ii);
264  rcl_wait_set.clients[ii] = nullptr;
265  if (ret) {
266  break;
267  }
268  }
269  }
270  }
271  return ret;
272  }
273 
275  std::shared_ptr<rclcpp::Waitable>
277  {
278  check_wait_result_dirty();
279  auto waitable = std::shared_ptr<rclcpp::Waitable>{nullptr};
280  auto data = std::shared_ptr<void>{nullptr};
281 
282  if (this->kind() == WaitResultKind::Ready) {
283  auto & wait_set = this->get_wait_set();
284  auto & rcl_wait_set = wait_set.get_rcl_wait_set();
285  while (next_waitable_index_ < wait_set.size_of_waitables()) {
286  auto cur_waitable = wait_set.waitables(next_waitable_index_++);
287  if (cur_waitable != nullptr && cur_waitable->is_ready(rcl_wait_set)) {
288  waitable = cur_waitable;
289  break;
290  }
291  }
292  }
293 
294  return waitable;
295  }
296 
297 private:
298  RCLCPP_DISABLE_COPY(WaitResult)
299 
300  explicit WaitResult(WaitResultKind wait_result_kind)
301  : wait_result_kind_(wait_result_kind)
302  {
303  // Should be enforced by the static factory methods on this class.
304  assert(WaitResultKind::Ready != wait_result_kind);
305  }
306 
307  WaitResult(WaitResultKind wait_result_kind, WaitSetT & wait_set)
308  : wait_result_kind_(wait_result_kind),
309  wait_set_pointer_(&wait_set)
310  {
311  // Should be enforced by the static factory methods on this class.
312  assert(WaitResultKind::Ready == wait_result_kind);
313  // Secure thread-safety (if provided) and shared ownership (if needed).
314  this->get_wait_set().wait_result_acquire();
315  }
316 
318  void
319  check_wait_result_dirty()
320  {
321  // In the case that the wait set was modified while the result was out,
322  // we must mark the wait result as no longer valid
323  if (wait_set_pointer_ && this->get_wait_set().wait_result_dirty_) {
324  this->wait_result_kind_ = WaitResultKind::Invalid;
325  }
326  }
327 
328  WaitResultKind wait_result_kind_;
329 
330  WaitSetT * wait_set_pointer_ = nullptr;
331 
332  size_t next_waitable_index_ = 0;
333 };
334 
335 } // namespace rclcpp
336 
337 #endif // RCLCPP__WAIT_RESULT_HPP_
Interface for introspecting a wait set after waiting on it.
Definition: wait_result.hpp:62
std::shared_ptr< rclcpp::SubscriptionBase > next_ready_subscription()
Get the next ready subscription, clearing it from the wait result.
std::pair< std::shared_ptr< rclcpp::TimerBase >, size_t > peek_next_ready_timer(size_t start_index=0)
Get the next ready timer and its index in the wait result, but do not clear it.
static WaitResult from_ready_wait_result_kind(WaitSetT &wait_set)
Create WaitResult from a "ready" result.
Definition: wait_result.hpp:72
static WaitResult from_empty_wait_result_kind()
Create WaitResult from a "empty" result.
Definition: wait_result.hpp:88
static WaitResult from_timeout_wait_result_kind()
Create WaitResult from a "timeout" result.
Definition: wait_result.hpp:80
std::shared_ptr< rclcpp::ServiceBase > next_ready_service()
Get the next ready service, clearing it from the wait result.
const WaitSetT & get_wait_set() const
Return the rcl wait set.
WaitResultKind kind() const
Return the kind of the WaitResult.
Definition: wait_result.hpp:95
void clear_timer_with_index(size_t index)
Clear the timer at the given index.
std::shared_ptr< rclcpp::ClientBase > next_ready_client()
Get the next ready client, clearing it from the wait result.
WaitSetT & get_wait_set()
Return the rcl wait set.
std::shared_ptr< rclcpp::Waitable > next_ready_waitable()
Get the next ready waitable, clearing it from the wait result.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.