ros2_control - rolling
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
159 template <typename U = T>
160 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> try_get() const
161 {
162 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
163 if (!guard.try_lock()) {
164 return std::nullopt;
165 }
166 return value_;
167 }
168
174 bool try_get(const std::function<void(const T &)> & func)
175 {
176 std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
177 if (!guard.try_lock()) {
178 return false;
179 }
180
181 func(value_);
182 return true;
183 }
184
190 template <typename U = T>
191 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> set(const T & value)
192 {
193 std::lock_guard<mutex_t> guard(lock_);
194 // cppcheck-suppress missingReturn
195 value_ = value;
196 }
197
201 void set(const std::function<void(T &)> & func)
202 {
203 std::lock_guard<mutex_t> guard(lock_);
204 if (!func) {
205 if constexpr (is_ptr_or_smart_ptr<T>) {
206 value_ = nullptr;
207 return;
208 }
209 }
210 func(value_);
211 }
212
217 template <typename U = T>
218 [[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
219 {
220 std::lock_guard<mutex_t> guard(lock_);
221 return value_;
222 }
223
228 template <typename U = T>
229 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
230 {
231 std::lock_guard<mutex_t> guard(lock_);
232 // cppcheck-suppress missingReturn
233 in = value_;
234 }
235
241 void get(const std::function<void(const T &)> & func)
242 {
243 std::lock_guard<mutex_t> guard(lock_);
244 func(value_);
245 }
246
251 template <typename U = T>
252 typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
253 {
254 set(value);
255 }
256
261 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
262 [[nodiscard]] operator T() const
263 {
264 // Only makes sense with the getNonRT method otherwise we would return an std::optional
265 return get();
266 }
267
272 template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
273 [[nodiscard]] operator std::optional<T>() const
274 {
275 return try_get();
276 }
277
278 // In case one wants to actually use a pointer
279 // in this implementation we allow accessing the lock directly.
280 // Note: Be careful with lock.unlock().
281 // It may only be called from the thread that locked the mutex!
282 [[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
283 [[nodiscard]] mutex_t & get_mutex() { return lock_; }
284
285private:
286 T value_;
287
288 // Protects access to the thing in the box. This mutex is
289 // guaranteed to be locked for no longer than the duration of the
290 // copy, so as long as the copy is realtime safe and the OS has
291 // priority inheritance for mutexes, this lock can be safely locked
292 // from within realtime.
293 mutable mutex_t lock_;
294};
295
296// Introduce some easier to use names
297
298// Provide specialisations for different mutex types
299template <typename T>
300using RealtimeBoxStandard = RealtimeBoxBase<T, std::mutex>;
301
302template <typename T>
303using RealtimeBoxRecursive = RealtimeBoxBase<T, std::recursive_mutex>;
304
305// This is the specialisation we recommend to use in the end
306template <typename T>
307using RealtimeBox = RealtimeBoxStandard<T>;
308
309} // namespace realtime_tools
310
311#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:241
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:229
void set(const std::function< void(T &)> &func)
wait until the mutex could be locked and access the content (rw)
Definition realtime_box.hpp:201
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 > operator=(const T &value)
provide a custom assignment operator for easier usage
Definition realtime_box.hpp:252
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:191
bool try_get(const std::function< void(const T &)> &func)
access the content (r) with best effort
Definition realtime_box.hpp:174
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:160
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:218
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:38