ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
thread_safe_synchronization.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_SET_POLICIES__THREAD_SAFE_SYNCHRONIZATION_HPP_
16 #define RCLCPP__WAIT_SET_POLICIES__THREAD_SAFE_SYNCHRONIZATION_HPP_
17 
18 #include <chrono>
19 #include <functional>
20 #include <memory>
21 #include <utility>
22 
23 #include "rclcpp/client.hpp"
24 #include "rclcpp/exceptions.hpp"
25 #include "rclcpp/guard_condition.hpp"
26 #include "rclcpp/macros.hpp"
27 #include "rclcpp/service.hpp"
28 #include "rclcpp/subscription_base.hpp"
29 #include "rclcpp/subscription_wait_set_mask.hpp"
30 #include "rclcpp/timer.hpp"
31 #include "rclcpp/visibility_control.hpp"
32 #include "rclcpp/wait_result_kind.hpp"
33 #include "rclcpp/wait_set_policies/detail/synchronization_policy_common.hpp"
34 #include "rclcpp/wait_set_policies/detail/write_preferring_read_write_lock.hpp"
35 #include "rclcpp/waitable.hpp"
36 
37 namespace rclcpp
38 {
39 namespace wait_set_policies
40 {
41 
43 
67 {
68 protected:
69  explicit ThreadSafeSynchronization(rclcpp::Context::SharedPtr context)
70  : extra_guard_conditions_{{std::make_shared<rclcpp::GuardCondition>(context)}},
71  wprw_lock_([this]() {this->interrupt_waiting_wait_set();})
72  {}
73  ~ThreadSafeSynchronization() = default;
74 
76 
80  const std::array<std::shared_ptr<rclcpp::GuardCondition>, 1> &
82  {
83  return extra_guard_conditions_;
84  }
85 
87 
90  void
92  {
93  extra_guard_conditions_[0]->trigger();
94  }
95 
97  void
99  std::shared_ptr<rclcpp::SubscriptionBase> && subscription,
100  const rclcpp::SubscriptionWaitSetMask & mask,
101  std::function<
102  void(std::shared_ptr<rclcpp::SubscriptionBase>&&, const rclcpp::SubscriptionWaitSetMask &)
103  > add_subscription_function)
104  {
106  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
107  add_subscription_function(std::move(subscription), mask);
108  }
109 
111  void
113  std::shared_ptr<rclcpp::SubscriptionBase> && subscription,
114  const rclcpp::SubscriptionWaitSetMask & mask,
115  std::function<
116  void(std::shared_ptr<rclcpp::SubscriptionBase>&&, const rclcpp::SubscriptionWaitSetMask &)
117  > remove_subscription_function)
118  {
120  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
121  remove_subscription_function(std::move(subscription), mask);
122  }
123 
125  void
127  std::shared_ptr<rclcpp::GuardCondition> && guard_condition,
128  std::function<void(std::shared_ptr<rclcpp::GuardCondition>&&)> add_guard_condition_function)
129  {
131  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
132  add_guard_condition_function(std::move(guard_condition));
133  }
134 
136  void
138  std::shared_ptr<rclcpp::GuardCondition> && guard_condition,
139  std::function<void(std::shared_ptr<rclcpp::GuardCondition>&&)> remove_guard_condition_function)
140  {
142  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
143  remove_guard_condition_function(std::move(guard_condition));
144  }
145 
147  void
149  std::shared_ptr<rclcpp::TimerBase> && timer,
150  std::function<void(std::shared_ptr<rclcpp::TimerBase>&&)> add_timer_function)
151  {
153  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
154  add_timer_function(std::move(timer));
155  }
156 
158  void
160  std::shared_ptr<rclcpp::TimerBase> && timer,
161  std::function<void(std::shared_ptr<rclcpp::TimerBase>&&)> remove_timer_function)
162  {
164  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
165  remove_timer_function(std::move(timer));
166  }
167 
169  void
171  std::shared_ptr<rclcpp::ClientBase> && client,
172  std::function<void(std::shared_ptr<rclcpp::ClientBase>&&)> add_client_function)
173  {
175  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
176  add_client_function(std::move(client));
177  }
178 
180  void
182  std::shared_ptr<rclcpp::ClientBase> && client,
183  std::function<void(std::shared_ptr<rclcpp::ClientBase>&&)> remove_client_function)
184  {
186  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
187  remove_client_function(std::move(client));
188  }
189 
191  void
193  std::shared_ptr<rclcpp::ServiceBase> && service,
194  std::function<void(std::shared_ptr<rclcpp::ServiceBase>&&)> add_service_function)
195  {
197  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
198  add_service_function(std::move(service));
199  }
200 
202  void
204  std::shared_ptr<rclcpp::ServiceBase> && service,
205  std::function<void(std::shared_ptr<rclcpp::ServiceBase>&&)> remove_service_function)
206  {
208  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
209  remove_service_function(std::move(service));
210  }
211 
213  void
215  std::shared_ptr<rclcpp::Waitable> && waitable,
216  std::shared_ptr<void> && associated_entity,
217  std::function<
218  void(std::shared_ptr<rclcpp::Waitable>&&, std::shared_ptr<void> &&)
219  > add_waitable_function)
220  {
222  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
223  add_waitable_function(std::move(waitable), std::move(associated_entity));
224  }
225 
227  void
229  std::shared_ptr<rclcpp::Waitable> && waitable,
230  std::function<void(std::shared_ptr<rclcpp::Waitable>&&)> remove_waitable_function)
231  {
233  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
234  remove_waitable_function(std::move(waitable));
235  }
236 
238  void
239  sync_prune_deleted_entities(std::function<void()> prune_deleted_entities_function)
240  {
242  std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
243  prune_deleted_entities_function();
244  }
245 
247  template<class WaitResultT>
248  WaitResultT
250  std::chrono::nanoseconds time_to_wait_ns,
251  std::function<void()> rebuild_rcl_wait_set,
252  std::function<rcl_wait_set_t & ()> get_rcl_wait_set,
253  std::function<WaitResultT(WaitResultKind wait_result_kind)> create_wait_result)
254  {
255  // Assumption: this function assumes that some measure has been taken to
256  // ensure none of the entities being waited on by the wait set are allowed
257  // to go out of scope and therefore be deleted.
258  // In the case of the StaticStorage policy, this is ensured because it
259  // retains shared ownership of all entites for the duration of its own life.
260  // In the case of the DynamicStorage policy, this is ensured by the function
261  // which calls this function, by acquiring shared ownership of the entites
262  // for the duration of this function.
263 
264  // Setup looping predicate.
265  auto start = std::chrono::steady_clock::now();
266  std::function<bool()> should_loop = this->create_loop_predicate(time_to_wait_ns, start);
267 
268  // Wait until exit condition is met.
269  do {
270  {
271  // We have to prevent the entity sets from being mutated while building
272  // the rcl wait set.
274  std::lock_guard<WritePreferringReadWriteLock::ReadMutex> lock(wprw_lock_.get_read_mutex());
275  // Rebuild the wait set.
276  // This will resize the wait set if needed, due to e.g. adding or removing
277  // entities since the last wait, but this should never occur in static
278  // storage wait sets since they cannot be changed after construction.
279  // This will also clear the wait set and re-add all the entities, which
280  // prepares it to be waited on again.
281  rebuild_rcl_wait_set();
282  }
283 
284  rcl_wait_set_t & rcl_wait_set = get_rcl_wait_set();
285 
286  // Wait unconditionally until timeout condition occurs since we assume
287  // there are no conditions that would require the wait to stop and reset,
288  // like asynchronously adding or removing an entity, i.e. explicitly
289  // providing no thread-safety.
290 
291  // Calculate how much time there is left to wait, unless blocking indefinitely.
292  auto time_left_to_wait_ns = this->calculate_time_left_to_wait(time_to_wait_ns, start);
293 
294  // Then wait for entities to become ready.
295 
296  // It is ok to wait while not having the lock acquired, because the state
297  // in the rcl wait set will not be updated until this method calls
298  // rebuild_rcl_wait_set().
299  rcl_ret_t ret = rcl_wait(&rcl_wait_set, time_left_to_wait_ns.count());
300  if (RCL_RET_OK == ret) {
301  // Something has become ready in the wait set, first check if it was
302  // the guard condition added by this class and/or a user defined guard condition.
303  const rcl_guard_condition_t * interrupt_guard_condition_ptr =
304  &(extra_guard_conditions_[0]->get_rcl_guard_condition());
305  bool was_interrupted_by_this_class = false;
306  bool any_user_guard_conditions_triggered = false;
307  for (size_t index = 0; index < rcl_wait_set.size_of_guard_conditions; ++index) {
308  const rcl_guard_condition_t * current = rcl_wait_set.guard_conditions[index];
309  if (nullptr != current) {
310  // Something is ready.
311  if (rcl_wait_set.guard_conditions[index] == interrupt_guard_condition_ptr) {
312  // This means that this class triggered a guard condition to interrupt this wait.
313  was_interrupted_by_this_class = true;
314  } else {
315  // This means it was a user guard condition.
316  any_user_guard_conditions_triggered = true;
317  }
318  }
319  }
320 
321  if (!was_interrupted_by_this_class || any_user_guard_conditions_triggered) {
322  // In this case we know:
323  // - something was ready
324  // - it was either:
325  // - not interrupted by this class, or
326  // - maybe it was, but there were also user defined guard conditions.
327  //
328  // We cannot ignore user defined guard conditions, but we can ignore
329  // other kinds of user defined entities, because they will still be
330  // ready next time we wait, whereas guard conditions are cleared.
331  // Therefore we need to create a WaitResult and return it.
332 
333  // The WaitResult will call sync_wait_result_acquire() and
334  // sync_wait_result_release() to ensure thread-safety by preventing
335  // the mutation of the entity sets while introspecting after waiting.
336  return create_wait_result(WaitResultKind::Ready);
337  }
338  // If we get here the we interrupted the wait set and there were no user
339  // guard conditions that needed to be handled.
340  // So we will loop and it will re-acquire the lock and rebuild the
341  // rcl wait set.
342  } else if (RCL_RET_TIMEOUT == ret) {
343  // The wait set timed out, exit the loop.
344  break;
345  } else if (RCL_RET_WAIT_SET_EMPTY == ret) {
346  // Wait set was empty, return Empty.
347  return create_wait_result(WaitResultKind::Empty);
348  } else {
349  // Some other error case, throw.
350  rclcpp::exceptions::throw_from_rcl_error(ret);
351  }
352  } while (should_loop());
353 
354  // Wait did not result in ready items, return timeout.
355  return create_wait_result(WaitResultKind::Timeout);
356  }
357 
358  void
359  sync_wait_result_acquire()
360  {
361  wprw_lock_.get_read_mutex().lock();
362  }
363 
364  void
365  sync_wait_result_release()
366  {
367  wprw_lock_.get_read_mutex().unlock();
368  }
369 
370 protected:
371  std::array<std::shared_ptr<rclcpp::GuardCondition>, 1> extra_guard_conditions_;
373 };
374 
375 } // namespace wait_set_policies
376 } // namespace rclcpp
377 
378 #endif // RCLCPP__WAIT_SET_POLICIES__THREAD_SAFE_SYNCHRONIZATION_HPP_
Options used to determine what parts of a subscription get added to or removed from a wait set.
WaitSet policy that provides thread-safe synchronization for the wait set.
const std::array< std::shared_ptr< rclcpp::GuardCondition >, 1 > & get_extra_guard_conditions()
Return any "extra" guard conditions needed to implement the synchronization policy.
void sync_add_service(std::shared_ptr< rclcpp::ServiceBase > &&service, std::function< void(std::shared_ptr< rclcpp::ServiceBase > &&)> add_service_function)
Add service.
void sync_remove_guard_condition(std::shared_ptr< rclcpp::GuardCondition > &&guard_condition, std::function< void(std::shared_ptr< rclcpp::GuardCondition > &&)> remove_guard_condition_function)
Remove guard condition.
void sync_remove_timer(std::shared_ptr< rclcpp::TimerBase > &&timer, std::function< void(std::shared_ptr< rclcpp::TimerBase > &&)> remove_timer_function)
Remove timer.
void sync_remove_waitable(std::shared_ptr< rclcpp::Waitable > &&waitable, std::function< void(std::shared_ptr< rclcpp::Waitable > &&)> remove_waitable_function)
Remove waitable.
void sync_add_timer(std::shared_ptr< rclcpp::TimerBase > &&timer, std::function< void(std::shared_ptr< rclcpp::TimerBase > &&)> add_timer_function)
Add timer.
void sync_remove_subscription(std::shared_ptr< rclcpp::SubscriptionBase > &&subscription, const rclcpp::SubscriptionWaitSetMask &mask, std::function< void(std::shared_ptr< rclcpp::SubscriptionBase > &&, const rclcpp::SubscriptionWaitSetMask &) > remove_subscription_function)
Remove guard condition.
void sync_remove_service(std::shared_ptr< rclcpp::ServiceBase > &&service, std::function< void(std::shared_ptr< rclcpp::ServiceBase > &&)> remove_service_function)
Remove service.
void sync_remove_client(std::shared_ptr< rclcpp::ClientBase > &&client, std::function< void(std::shared_ptr< rclcpp::ClientBase > &&)> remove_client_function)
Remove client.
void sync_prune_deleted_entities(std::function< void()> prune_deleted_entities_function)
Prune deleted entities.
void sync_add_waitable(std::shared_ptr< rclcpp::Waitable > &&waitable, std::shared_ptr< void > &&associated_entity, std::function< void(std::shared_ptr< rclcpp::Waitable > &&, std::shared_ptr< void > &&) > add_waitable_function)
Add waitable.
void sync_add_client(std::shared_ptr< rclcpp::ClientBase > &&client, std::function< void(std::shared_ptr< rclcpp::ClientBase > &&)> add_client_function)
Add client.
void sync_add_guard_condition(std::shared_ptr< rclcpp::GuardCondition > &&guard_condition, std::function< void(std::shared_ptr< rclcpp::GuardCondition > &&)> add_guard_condition_function)
Add guard condition.
WaitResultT sync_wait(std::chrono::nanoseconds time_to_wait_ns, std::function< void()> rebuild_rcl_wait_set, std::function< rcl_wait_set_t &()> get_rcl_wait_set, std::function< WaitResultT(WaitResultKind wait_result_kind)> create_wait_result)
Implements wait.
void sync_add_subscription(std::shared_ptr< rclcpp::SubscriptionBase > &&subscription, const rclcpp::SubscriptionWaitSetMask &mask, std::function< void(std::shared_ptr< rclcpp::SubscriptionBase > &&, const rclcpp::SubscriptionWaitSetMask &) > add_subscription_function)
Add subscription.
void interrupt_waiting_wait_set()
Interrupt any waiting wait set.
RCLCPP_PUBLIC WriteMutex & get_write_mutex()
Return write mutex which can be used with standard constructs like std::lock_guard.
RCLCPP_PUBLIC ReadMutex & get_read_mutex()
Return read mutex which can be used with standard constructs like std::lock_guard.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
Handle for a rcl guard condition.
Container for subscription's, guard condition's, etc to be waited on.
Definition: wait.h:42
size_t size_of_guard_conditions
Number of guard_conditions.
Definition: wait.h:50
const rcl_guard_condition_t ** guard_conditions
Storage for guard condition pointers.
Definition: wait.h:48
#define RCL_RET_WAIT_SET_EMPTY
Given rcl_wait_set_t is empty return code.
Definition: types.h:101
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_TIMEOUT
Timeout occurred return code.
Definition: types.h:31
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_wait(rcl_wait_set_t *wait_set, int64_t timeout)
Block until the wait set is ready or until the timeout has been exceeded.
Definition: wait.c:538