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
262#ifndef _WIN32
263 // TODO(anyone): Fix MSVC issues with SFINAE and enable the code below
264
271 template <typename U = T>
272 [[deprecated("Use set(const std::function<void(T &)> & func) instead!")]]
273 typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> set(const T & value)
274 {
275 std::lock_guard<mutex_t> guard(lock_);
276 // cppcheck-suppress missingReturn
277 value_ = value;
278 }
279
284 template <
285 typename F,
286 typename = std::enable_if_t<std::is_invocable_v<F, T &> && !std::is_invocable_v<F, T>>>
287 void set(F && func)
288 {
289 std::lock_guard<mutex_t> guard(lock_);
290 std::forward<F>(func)(value_);
291 }
292
297 template <typename U = T, typename = std::enable_if_t<is_ptr_or_smart_ptr<U>>>
298 void set(std::nullptr_t)
299 {
300 std::lock_guard<mutex_t> guard(lock_);
301 value_ = nullptr;
302 }
303
304#else
309 void set(const std::function<void(T &)> & func)
310 {
311 std::lock_guard<mutex_t> guard(lock_);
312 if (!func) {
313 if constexpr (is_ptr_or_smart_ptr<T>) {
314 value_ = nullptr;
315 return;
316 }
317 }
318 func(value_);
319 }
320#endif
321
326 template <typename U = T>
327 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
328 {
329 std::lock_guard<mutex_t> guard(lock_);
330 return value_;
331 }
332
337 template <typename U = T>
338 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
339 {
340 std::lock_guard<mutex_t> guard(lock_);
341 // cppcheck-suppress missingReturn
342 in = value_;
343 }
344
351 template <typename U = T>
352 [[deprecated("Use get(const std::function<void(const T &)> & func) instead!")]]
353 typename std::enable_if_t<is_ptr_or_smart_ptr<U>, void> get(T & in) const
354 {
355 std::lock_guard<mutex_t> guard(lock_);
356 // cppcheck-suppress missingReturn
357 in = value_;
358 }
359
365 void get(const std::function<void(const T &)> & func)
366 {
367 std::lock_guard<mutex_t> guard(lock_);
368 func(value_);
369 }
370
375 template <typename U = T>
376 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
377 {
378 set(value);
379 }
380
385 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
386 [[nodiscard]] operator T() const
387 {
388 // Only makes sense with the getNonRT method otherwise we would return an std::optional
389 return get();
390 }
391
396 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
397 [[nodiscard]] operator std::optional<T>() const
398 {
399 return try_get();
400 }
401
402 // In case one wants to actually use a pointer
403 // in this implementation we allow accessing the lock directly.
404 // Note: Be careful with lock.unlock().
405 // It may only be called from the thread that locked the mutex!
406 [[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
407 [[nodiscard]] mutex_t & get_mutex() { return lock_; }
408
409 [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] mutex_t & getMutex() { return lock_; }
410 [[nodiscard]] [[deprecated("Use get_mutex() instead!")]] const mutex_t & getMutex() const
411 {
412 return lock_;
413 }
414
415private:
416 T value_;
417
418 // Protects access to the thing in the box. This mutex is
419 // guaranteed to be locked for no longer than the duration of the
420 // copy, so as long as the copy is realtime safe and the OS has
421 // priority inheritance for mutexes, this lock can be safely locked
422 // from within realtime.
423 mutable mutex_t lock_;
424};
425
426// Provide specialisations for other mutex types
427
428template <typename T>
429using RealtimeThreadSafeBoxRecursive = RealtimeThreadSafeBox<T, RECURSIVE_MUTEX>;
430
431} // namespace realtime_tools
432
433#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:273
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:376
void set(F &&func)
wait until the mutex could be locked and access the content (rw)
Definition realtime_thread_safe_box.hpp:287
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:353
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: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_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:298
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:365
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:338
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:40