ros2_control - iron
Loading...
Searching...
No Matches
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
44namespace realtime_tools
45{
46
47template <typename T>
48constexpr auto is_ptr_or_smart_ptr = rcpputils::is_pointer<T>::value;
49
58template <class T, typename mutex_type = std::mutex>
60{
61 static_assert(std::is_copy_constructible_v<T>, "Passed type must be copy constructible");
62
63public:
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
377private:
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
391template <typename T, typename mutex_type = std::mutex>
392using RealtimeBoxBestEffort [[deprecated("Use RealtimeBox instead")]] =
393 RealtimeBoxBase<T, mutex_type>;
394
395// Provide specialisations for different mutex types
396template <typename T>
397using RealtimeBoxStandard = RealtimeBoxBase<T, std::mutex>;
398
399template <typename T>
400using RealtimeBoxRecursive = RealtimeBoxBase<T, std::recursive_mutex>;
401
402// This is the specialisation we recommend to use in the end
403template <typename T>
404using 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 >, void > set(const T &value)
Wait until the mutex can be locked and set the content (RealtimeBox behavior)
Definition realtime_box.hpp:262
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 >, bool > trySet(const T &value)
set a new content with best effort
Definition realtime_box.hpp:163
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 >, 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 >, 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 >, 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 >, std::optional< U > > tryGet() const
get the content with best effort
Definition realtime_box.hpp:223
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 >, 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 >, U > get() const
Wait until the mutex could be locked and get the content (RealtimeBox behaviour)
Definition realtime_box.hpp:289
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:38