ros2_control - jazzy
Loading...
Searching...
No Matches
realtime_thread_safe_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_THREAD_SAFE_BOX_HPP_
34#define REALTIME_TOOLS__REALTIME_THREAD_SAFE_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#ifndef _WIN32
44#include "realtime_tools/mutex.hpp"
45#define DEFAULT_MUTEX realtime_tools::prio_inherit_mutex
46#define RECURSIVE_MUTEX realtime_tools::prio_inherit_recursive_mutex
47#else
48#define DEFAULT_MUTEX std::mutex
49#define RECURSIVE_MUTEX std::recursive_mutex
50#endif
51
52namespace realtime_tools
53{
54
55template <typename T>
56constexpr auto is_ptr_or_smart_ptr = rcpputils::is_pointer<T>::value;
57
66template <class T, typename mutex_type = DEFAULT_MUTEX>
68{
69 static_assert(std::is_copy_constructible_v<T>, "Passed type must be copy constructible");
70
71public:
72 using mutex_t = mutex_type;
73 using type = T;
74 // Provide various constructors
75 constexpr explicit RealtimeThreadSafeBox(const T & init = T{}) : value_(init) {}
76 constexpr explicit RealtimeThreadSafeBox(const T && init) : value_(std::move(init)) {}
77
78 // Copy constructor
80 {
81 // Lock the other box mutex
82 std::unique_lock<mutex_t> lock(o.lock_);
83 // We do not need to lock our own mutex because we are currently in the process of being created
84 value_ = o.value_;
85 }
86
87 // Copy assignment constructor
88 constexpr RealtimeThreadSafeBox & operator=(const RealtimeThreadSafeBox & o)
89 {
90 // Check for self assignment (and a potential deadlock)
91 if (&o != this) {
92 // Lock the other box mutex
93 std::unique_lock<mutex_t> lock_other(o.lock_);
94 std::unique_lock<mutex_t> lock_self(lock_);
95
96 value_ = o.value_;
97 }
98 return *this;
99 }
100
102 {
103 // Lock the other box mutex
104 std::unique_lock<mutex_t> lock(o.lock_);
105 // We do not need to lock our own mutex because we are currently in the process of being created
106 value_ = std::move(o.value_);
107 }
108
109 // Only enabled for types that can be constructed from an initializer list
110 template <typename U = T>
111 constexpr RealtimeThreadSafeBox(
112 const std::initializer_list<U> & init,
113 std::enable_if_t<std::is_constructible_v<U, std::initializer_list<U>>>)
114 : value_(init)
115 {
116 }
117
118 constexpr RealtimeThreadSafeBox & operator=(RealtimeThreadSafeBox && o)
119 {
120 // Check for self assignment (and a potential deadlock)
121 if (&o != this) {
122 // Lock the other box mutex
123 std::unique_lock<mutex_t> lock_other(o.lock_);
124 std::unique_lock<mutex_t> lock_self(lock_);
125
126 value_ = std::move(o.value_);
127 }
128 return *this;
129 }
130
136 template <typename U = T>
137 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, bool> try_set(const T & value)
138 {
139 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
140 if (!guard.try_lock()) {
141 return false;
142 }
143 value_ = value;
144 return true;
145 }
146
152 bool try_set(const std::function<void(T &)> & func)
153 {
154 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
155 if (!guard.try_lock()) {
156 return false;
157 }
158
159 func(value_);
160 return true;
161 }
162
169 template <typename U = T>
170 [[deprecated("Use try_set(const T & value) instead!")]]
171 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, bool> trySet(const T & value)
172 {
173 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
174 if (!guard.try_lock()) {
175 return false;
176 }
177 value_ = value;
178 return true;
179 }
180
187 template <typename U = T>
188 [[deprecated("Use try_set(const std::function<void(T &)> & func) instead!")]]
189 bool trySet(const std::function<void(T &)> & func)
190 {
191 return try_set(func);
192 }
193
198 template <typename U = T>
199 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> try_get() const
200 {
201 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
202 if (!guard.try_lock()) {
203 return std::nullopt;
204 }
205 return value_;
206 }
207
213 bool try_get(const std::function<void(const T &)> & func)
214 {
215 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
216 if (!guard.try_lock()) {
217 return false;
218 }
219
220 func(value_);
221 return true;
222 }
223
229 template <typename U = T>
230 [[deprecated("Use try_get() instead!")]] [[nodiscard]]
231 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> tryGet() const
232 {
233 return try_get();
234 }
235
242 template <typename U = T>
243 [[deprecated("Use try_get(const std::function<void(const T &)> & func) instead!")]]
244 bool tryGet(const std::function<void(const T &)> & func)
245 {
246 return try_get(func);
247 }
248
254 template <typename U = T>
255 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> set(const T & value)
256 {
257 std::lock_guard<mutex_t> guard(lock_);
258 // cppcheck-suppress missingReturn
259 value_ = value;
260 }
261
268 template <typename U = T>
269 [[deprecated("Use set(const std::function<void(T &)> & func) instead!")]]
270 typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> set(const T & value)
271 {
272 std::lock_guard<mutex_t> guard(lock_);
273 // cppcheck-suppress missingReturn
274 value_ = value;
275 }
276
281 template <
282 typename F,
283 typename = std::enable_if_t<std::is_invocable_v<F, T &> && !std::is_invocable_v<F, T>>>
284 void set(F && func)
285 {
286 std::lock_guard<mutex_t> guard(lock_);
287 std::forward<F>(func)(value_);
288 }
289
294 template <typename U = T, typename = std::enable_if_t<is_ptr_or_smart_ptr<U>>>
295 void set(std::nullptr_t)
296 {
297 std::lock_guard<mutex_t> guard(lock_);
298 value_ = nullptr;
299 }
300
305 template <typename U = T>
306 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
307 {
308 std::lock_guard<mutex_t> guard(lock_);
309 return value_;
310 }
311
316 template <typename U = T>
317 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
318 {
319 std::lock_guard<mutex_t> guard(lock_);
320 // cppcheck-suppress missingReturn
321 in = value_;
322 }
323
330 template <typename U = T>
331 [[deprecated("Use get(const std::function<void(const T &)> & func) instead!")]]
332 typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> get(T & in) const
333 {
334 std::lock_guard<mutex_t> guard(lock_);
335 // cppcheck-suppress missingReturn
336 in = value_;
337 }
338
344 void get(const std::function<void(const T &)> & func)
345 {
346 std::lock_guard<mutex_t> guard(lock_);
347 func(value_);
348 }
349
354 template <typename U = T>
355 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
356 {
357 set(value);
358 }
359
364 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
365 [[nodiscard]] operator T() const
366 {
367 // Only makes sense with the getNonRT method otherwise we would return an std::optional
368 return get();
369 }
370
375 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
376 [[nodiscard]] operator std::optional<T>() const
377 {
378 return try_get();
379 }
380
381 // In case one wants to actually use a pointer
382 // in this implementation we allow accessing the lock directly.
383 // Note: Be careful with lock.unlock().
384 // It may only be called from the thread that locked the mutex!
385 [[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
386 [[nodiscard]] mutex_t & get_mutex() { return lock_; }
387
388 [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] mutex_t & getMutex() { return lock_; }
389 [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] const mutex_t & getMutex() const
390 {
391 return lock_;
392 }
393
394private:
395 T value_;
396
397 // Protects access to the thing in the box. This mutex is
398 // guaranteed to be locked for no longer than the duration of the
399 // copy, so as long as the copy is realtime safe and the OS has
400 // priority inheritance for mutexes, this lock can be safely locked
401 // from within realtime.
402 mutable mutex_t lock_;
403};
404
405// Provide specialisations for other mutex types
406
407template <typename T>
408using RealtimeThreadSafeBoxRecursive = RealtimeThreadSafeBox<T, RECURSIVE_MUTEX>;
409
410} // namespace realtime_tools
411
412#endif // REALTIME_TOOLS__REALTIME_THREAD_SAFE_BOX_HPP_
Definition realtime_thread_safe_box.hpp:68
std::enable_if_t<!is_ptr_or_smart_ptr< U >, std::optional< U > > tryGet() const
get the content with best effort
Definition realtime_thread_safe_box.hpp:231
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 (RealtimeThreadSafeBox behavior)
Definition realtime_thread_safe_box.hpp:270
bool try_set(const std::function< void(T &)> &func)
access the content readable with best effort
Definition realtime_thread_safe_box.hpp:152
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_thread_safe_box.hpp:355
void set(F &&func)
wait until the mutex could be locked and access the content (rw)
Definition realtime_thread_safe_box.hpp:284
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 (RealtimeThreadSafeBox behavior)
Definition realtime_thread_safe_box.hpp:255
bool tryGet(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition realtime_thread_safe_box.hpp:244
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_thread_safe_box.hpp:332
bool trySet(const std::function< void(T &)> &func)
access the content readable with best effort
Definition realtime_thread_safe_box.hpp:189
std::enable_if_t<!is_ptr_or_smart_ptr< U >, U > get() const
Wait until the mutex could be locked and get the content (RealtimeThreadSafeBox behaviour)
Definition realtime_thread_safe_box.hpp:306
std::enable_if_t<!is_ptr_or_smart_ptr< U >, bool > trySet(const T &value)
set a new content with best effort
Definition realtime_thread_safe_box.hpp:171
void set(std::nullptr_t)
wait until the mutex could be locked and access the content (rw)
Definition realtime_thread_safe_box.hpp:295
void get(const std::function< void(const T &)> &func)
Wait until the mutex could be locked and access the content (r)
Definition realtime_thread_safe_box.hpp:344
bool try_get(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition realtime_thread_safe_box.hpp:213
std::enable_if_t<!is_ptr_or_smart_ptr< U >, std::optional< U > > try_get() const
get the content with best effort
Definition realtime_thread_safe_box.hpp:199
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_thread_safe_box.hpp:317
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_thread_safe_box.hpp:137
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:38