ros2_control - kilted
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
167 template <typename U = T>
168 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> try_get() const
169 {
170 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
171 if (!guard.try_lock()) {
172 return std::nullopt;
173 }
174 return value_;
175 }
176
182 bool try_get(const std::function<void(const T &)> & func)
183 {
184 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
185 if (!guard.try_lock()) {
186 return false;
187 }
188
189 func(value_);
190 return true;
191 }
192
198 template <typename U = T>
199 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> set(const T & value)
200 {
201 std::lock_guard<mutex_t> guard(lock_);
202 // cppcheck-suppress missingReturn
203 value_ = value;
204 }
205
206#ifndef _WIN32
207 // TODO(anyone): Fix MSVC issues with SFINAE and enable the code below
208
213 template <
214 typename F,
215 typename = std::enable_if_t<std::is_invocable_v<F, T &> && !std::is_invocable_v<F, T>>>
216 void set(F && func)
217 {
218 std::lock_guard<mutex_t> guard(lock_);
219 std::forward<F>(func)(value_);
220 }
221
226 template <typename U = T, typename = std::enable_if_t<is_ptr_or_smart_ptr<U>>>
227 void set(std::nullptr_t)
228 {
229 std::lock_guard<mutex_t> guard(lock_);
230 value_ = nullptr;
231 }
232
233#else
238 void set(const std::function<void(T &)> & func)
239 {
240 std::lock_guard<mutex_t> guard(lock_);
241 if (!func) {
242 if constexpr (is_ptr_or_smart_ptr<T>) {
243 value_ = nullptr;
244 return;
245 }
246 }
247 func(value_);
248 }
249#endif
250
255 template <typename U = T>
256 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
257 {
258 std::lock_guard<mutex_t> guard(lock_);
259 return value_;
260 }
261
266 template <typename U = T>
267 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
268 {
269 std::lock_guard<mutex_t> guard(lock_);
270 // cppcheck-suppress missingReturn
271 in = value_;
272 }
273
279 void get(const std::function<void(const T &)> & func)
280 {
281 std::lock_guard<mutex_t> guard(lock_);
282 func(value_);
283 }
284
289 template <typename U = T>
290 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
291 {
292 set(value);
293 }
294
299 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
300 [[nodiscard]] operator T() const
301 {
302 // Only makes sense with the getNonRT method otherwise we would return an std::optional
303 return get();
304 }
305
310 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
311 [[nodiscard]] operator std::optional<T>() const
312 {
313 return try_get();
314 }
315
316 // In case one wants to actually use a pointer
317 // in this implementation we allow accessing the lock directly.
318 // Note: Be careful with lock.unlock().
319 // It may only be called from the thread that locked the mutex!
320 [[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
321 [[nodiscard]] mutex_t & get_mutex() { return lock_; }
322
323private:
324 T value_;
325
326 // Protects access to the thing in the box. This mutex is
327 // guaranteed to be locked for no longer than the duration of the
328 // copy, so as long as the copy is realtime safe and the OS has
329 // priority inheritance for mutexes, this lock can be safely locked
330 // from within realtime.
331 mutable mutex_t lock_;
332};
333
334// Provide specialisations for other mutex types
335
336template <typename T>
337using RealtimeThreadSafeBoxRecursive = RealtimeThreadSafeBox<T, RECURSIVE_MUTEX>;
338
339} // namespace realtime_tools
340
341#endif // REALTIME_TOOLS__REALTIME_THREAD_SAFE_BOX_HPP_
Definition realtime_thread_safe_box.hpp:68
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:290
void set(F &&func)
wait until the mutex could be locked and access the content (rw)
Definition realtime_thread_safe_box.hpp:216
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:199
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:256
void set(std::nullptr_t)
wait until the mutex could be locked and access the content (rw)
Definition realtime_thread_safe_box.hpp:227
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:279
bool try_get(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition realtime_thread_safe_box.hpp:182
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:168
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:267
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