ros2_control - rolling
realtime_box.hpp
1 // Copyright (c) 2009, Willow Garage, Inc.
2 // Copyright (c) 2024, Lennart Nachtigall
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of the Willow Garage, Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 
30 // Author: Stuart Glaser
31 // Author: Lennart Nachtigall
32 
33 #ifndef REALTIME_TOOLS__REALTIME_BOX_HPP_
34 #define REALTIME_TOOLS__REALTIME_BOX_HPP_
35 
36 #include <functional>
37 #include <initializer_list>
38 #include <mutex>
39 #include <optional>
40 #include <utility>
41 
42 #include <rcpputils/pointer_traits.hpp>
43 
44 namespace realtime_tools
45 {
46 
47 template <typename T>
48 constexpr auto is_ptr_or_smart_ptr = rcpputils::is_pointer<T>::value;
49 
58 template <class T, typename mutex_type = std::mutex>
60 {
61  static_assert(std::is_copy_constructible_v<T>, "Passed type must be copy constructible");
62 
63 public:
64  using mutex_t = mutex_type;
65  using type = T;
66  // Provide various constructors
67  constexpr explicit RealtimeBoxBase(const T & init = T{}) : value_(init) {}
68  constexpr explicit RealtimeBoxBase(const T && init) : value_(std::move(init)) {}
69 
70  // Copy constructor
71  constexpr RealtimeBoxBase(const RealtimeBoxBase & o)
72  {
73  // Lock the other box mutex
74  std::unique_lock<mutex_t> lock(o.lock_);
75  // We do not need to lock our own mutex because we are currently in the process of being created
76  value_ = o.value_;
77  }
78 
79  // Copy assignment constructor
80  constexpr RealtimeBoxBase & operator=(const RealtimeBoxBase & o)
81  {
82  // Check for self assignment (and a potential deadlock)
83  if (&o != this) {
84  // Lock the other box mutex
85  std::unique_lock<mutex_t> lock_other(o.lock_);
86  std::unique_lock<mutex_t> lock_self(lock_);
87 
88  value_ = o.value_;
89  }
90  return *this;
91  }
92 
93  constexpr RealtimeBoxBase(RealtimeBoxBase && o)
94  {
95  // Lock the other box mutex
96  std::unique_lock<mutex_t> lock(o.lock_);
97  // We do not need to lock our own mutex because we are currently in the process of being created
98  value_ = std::move(o.value_);
99  }
100 
101  // Only enabled for types that can be constructed from an initializer list
102  template <typename U = T>
103  constexpr RealtimeBoxBase(
104  const std::initializer_list<U> & init,
105  std::enable_if_t<std::is_constructible_v<U, std::initializer_list<U>>>)
106  : value_(init)
107  {
108  }
109 
110  constexpr RealtimeBoxBase & operator=(RealtimeBoxBase && o)
111  {
112  // Check for self assignment (and a potential deadlock)
113  if (&o != this) {
114  // Lock the other box mutex
115  std::unique_lock<mutex_t> lock_other(o.lock_);
116  std::unique_lock<mutex_t> lock_self(lock_);
117 
118  value_ = std::move(o.value_);
119  }
120  return *this;
121  }
122 
128  template <typename U = T>
129  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, bool> try_set(const T & value)
130  {
131  std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
132  if (!guard.try_lock()) {
133  return false;
134  }
135  value_ = value;
136  return true;
137  }
138 
144  bool try_set(const std::function<void(T &)> & func)
145  {
146  std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
147  if (!guard.try_lock()) {
148  return false;
149  }
150 
151  func(value_);
152  return true;
153  }
154 
161  template <typename U = T>
162  [[deprecated("Use try_set(const T & value) instead!")]]
163  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, bool> trySet(const T & value)
164  {
165  std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
166  if (!guard.try_lock()) {
167  return false;
168  }
169  value_ = value;
170  return true;
171  }
172 
179  template <typename U = T>
180  [[deprecated("Use try_set(const std::function<void(T &)> & func) instead!")]]
181  bool trySet(const std::function<void(T &)> & func)
182  {
183  return try_set(func);
184  }
185 
190  template <typename U = T>
191  [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> try_get() const
192  {
193  std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
194  if (!guard.try_lock()) {
195  return std::nullopt;
196  }
197  return value_;
198  }
199 
205  bool try_get(const std::function<void(const T &)> & func)
206  {
207  std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
208  if (!guard.try_lock()) {
209  return false;
210  }
211 
212  func(value_);
213  return true;
214  }
215 
221  template <typename U = T>
222  [[deprecated("Use try_get() instead!")]] [[nodiscard]]
223  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> tryGet() const
224  {
225  return try_get();
226  }
227 
234  template <typename U = T>
235  [[deprecated("Use try_get(const std::function<void(const T &)> & func) instead!")]]
236  bool tryGet(const std::function<void(const T &)> & func)
237  {
238  return try_get(func);
239  }
240 
246  template <typename U = T>
247  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> set(const T & value)
248  {
249  std::lock_guard<mutex_t> guard(lock_);
250  // cppcheck-suppress missingReturn
251  value_ = value;
252  }
253 
260  template <typename U = T>
261  [[deprecated("Use set(const std::function<void(T &)> & func) instead!")]]
262  typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> set(const T & value)
263  {
264  std::lock_guard<mutex_t> guard(lock_);
265  // cppcheck-suppress missingReturn
266  value_ = value;
267  }
268 
272  void set(const std::function<void(T &)> & func)
273  {
274  std::lock_guard<mutex_t> guard(lock_);
275  if (!func) {
276  if constexpr (is_ptr_or_smart_ptr<T>) {
277  value_ = nullptr;
278  return;
279  }
280  }
281  func(value_);
282  }
283 
288  template <typename U = T>
289  [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
290  {
291  std::lock_guard<mutex_t> guard(lock_);
292  return value_;
293  }
294 
299  template <typename U = T>
300  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
301  {
302  std::lock_guard<mutex_t> guard(lock_);
303  // cppcheck-suppress missingReturn
304  in = value_;
305  }
306 
313  template <typename U = T>
314  [[deprecated("Use get(const std::function<void(const T &)> & func) instead!")]]
315  typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> get(T & in) const
316  {
317  std::lock_guard<mutex_t> guard(lock_);
318  // cppcheck-suppress missingReturn
319  in = value_;
320  }
321 
327  void get(const std::function<void(const T &)> & func)
328  {
329  std::lock_guard<mutex_t> guard(lock_);
330  func(value_);
331  }
332 
337  template <typename U = T>
338  typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
339  {
340  set(value);
341  }
342 
347  template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
348  [[nodiscard]] operator T() const
349  {
350  // Only makes sense with the getNonRT method otherwise we would return an std::optional
351  return get();
352  }
353 
358  template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
359  [[nodiscard]] operator std::optional<T>() const
360  {
361  return try_get();
362  }
363 
364  // In case one wants to actually use a pointer
365  // in this implementation we allow accessing the lock directly.
366  // Note: Be careful with lock.unlock().
367  // It may only be called from the thread that locked the mutex!
368  [[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
369  [[nodiscard]] mutex_t & get_mutex() { return lock_; }
370 
371  [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] mutex_t & getMutex() { return lock_; }
372  [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] const mutex_t & getMutex() const
373  {
374  return lock_;
375  }
376 
377 private:
378  T value_;
379 
380  // Protects access to the thing in the box. This mutex is
381  // guaranteed to be locked for no longer than the duration of the
382  // copy, so as long as the copy is realtime safe and the OS has
383  // priority inheritance for mutexes, this lock can be safely locked
384  // from within realtime.
385  mutable mutex_t lock_;
386 };
387 
388 // Introduce some easier to use names
389 
390 // Only kept for compatibility reasons
391 template <typename T, typename mutex_type = std::mutex>
392 using RealtimeBoxBestEffort [[deprecated("Use RealtimeBox instead")]] =
393  RealtimeBoxBase<T, mutex_type>;
394 
395 // Provide specialisations for different mutex types
396 template <typename T>
397 using RealtimeBoxStandard = RealtimeBoxBase<T, std::mutex>;
398 
399 template <typename T>
400 using RealtimeBoxRecursive = RealtimeBoxBase<T, std::recursive_mutex>;
401 
402 // This is the specialisation we recommend to use in the end
403 template <typename T>
404 using RealtimeBox = RealtimeBoxStandard<T>;
405 
406 } // namespace realtime_tools
407 
408 #endif // REALTIME_TOOLS__REALTIME_BOX_HPP_
Definition: realtime_box.hpp:60
void get(const std::function< void(const T &)> &func)
Wait until the mutex could be locked and access the content (r)
Definition: realtime_box.hpp:327
std::enable_if_t<!is_ptr_or_smart_ptr< U >, bool > trySet(const T &value)
set a new content with best effort
Definition: realtime_box.hpp:163
std::enable_if_t<!is_ptr_or_smart_ptr< U >, std::optional< U > > tryGet() const
get the content with best effort
Definition: realtime_box.hpp:223
void set(const std::function< void(T &)> &func)
wait until the mutex could be locked and access the content (rw)
Definition: realtime_box.hpp:272
bool tryGet(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition: realtime_box.hpp:236
bool try_set(const std::function< void(T &)> &func)
access the content readable with best effort
Definition: realtime_box.hpp:144
std::enable_if_t<!is_ptr_or_smart_ptr< U >, void > get(T &in) const
Wait until the mutex could be locked and get the content (r)
Definition: realtime_box.hpp:300
std::enable_if_t<!is_ptr_or_smart_ptr< U >, U > get() const
Wait until the mutex could be locked and get the content (RealtimeBox behaviour)
Definition: realtime_box.hpp:289
std::enable_if_t<!is_ptr_or_smart_ptr< U >, void > operator=(const T &value)
provide a custom assignment operator for easier usage
Definition: realtime_box.hpp:338
std::enable_if_t<!is_ptr_or_smart_ptr< U >, void > set(const T &value)
Wait until the mutex can be locked and set the content (RealtimeBox behavior)
Definition: realtime_box.hpp:247
bool try_get(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition: realtime_box.hpp:205
std::enable_if_t< is_ptr_or_smart_ptr< U >, void > get(T &in) const
Wait until the mutex could be locked and get the content (r)
Definition: realtime_box.hpp:315
std::enable_if_t<!is_ptr_or_smart_ptr< U >, bool > try_set(const T &value)
set a new content with best effort
Definition: realtime_box.hpp:129
std::enable_if_t<!is_ptr_or_smart_ptr< U >, std::optional< U > > try_get() const
get the content with best effort
Definition: realtime_box.hpp:191
bool trySet(const std::function< void(T &)> &func)
access the content readable with best effort
Definition: realtime_box.hpp:181
std::enable_if_t< is_ptr_or_smart_ptr< U >, void > set(const T &value)
Wait until the mutex can be locked and set the content (RealtimeBox behavior)
Definition: realtime_box.hpp:262
Definition: async_function_handler.hpp:38