|
33 | 33 | #ifndef REALTIME_TOOLS__REALTIME_BOX_HPP_ |
34 | 34 | #define REALTIME_TOOLS__REALTIME_BOX_HPP_ |
35 | 35 |
|
36 | | -#include <functional> |
37 | | -#include <initializer_list> |
38 | | -#include <mutex> |
39 | | -#include <optional> |
40 | | -#include <utility> |
| 36 | +#include "realtime_tools/realtime_thread_safe_box.hpp" |
41 | 37 |
|
42 | | -#include <rcpputils/pointer_traits.hpp> |
| 38 | +// Deprecation notice |
| 39 | +#pragma message( \ |
| 40 | + "'RealtimeBox' is deprecated. Please update your code to use 'realtime_thread_safe_box.hpp' header, and class name 'RealtimeThreadSafeBox' instead.") //NOLINT |
43 | 41 |
|
44 | 42 | namespace realtime_tools |
45 | 43 | { |
46 | 44 |
|
47 | 45 | template <typename T> |
48 | | -constexpr auto is_ptr_or_smart_ptr = rcpputils::is_pointer<T>::value; |
49 | | - |
50 | | -/*! |
51 | | - A Box that ensures thread safe access to the boxed contents. |
52 | | - Access is best effort. If it can not lock it will return. |
53 | | -
|
54 | | - NOTE about pointers: |
55 | | - You can use pointers with this box but the access will be different. |
56 | | - Only use the get/set methods that take function pointer for accessing the internal value. |
57 | | -*/ |
58 | | -template <class T, typename mutex_type = std::mutex> |
59 | | -class RealtimeBoxBase |
60 | | -{ |
61 | | - static_assert(std::is_copy_constructible_v<T>, "Passed type must be copy constructible"); |
62 | | - |
63 | | -public: |
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 | | - |
123 | | - /** |
124 | | - * @brief set a new content with best effort |
125 | | - * @return false if mutex could not be locked |
126 | | - * @note disabled for pointer types |
127 | | - */ |
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 | | - |
139 | | - /** |
140 | | - * @brief access the content readable with best effort |
141 | | - * @return false if the mutex could not be locked |
142 | | - * @note only safe way to access pointer type content (rw) |
143 | | - */ |
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 | | - |
155 | | - /** |
156 | | - * @brief get the content with best effort |
157 | | - * @return std::nullopt if content could not be access, otherwise the content is returned |
158 | | - */ |
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 | | - |
169 | | - /** |
170 | | - * @brief access the content (r) with best effort |
171 | | - * @return false if the mutex could not be locked |
172 | | - * @note only safe way to access pointer type content (r) |
173 | | - */ |
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 | | - |
185 | | - /** |
186 | | - * @brief Wait until the mutex can be locked and set the content (RealtimeBox behavior) |
187 | | - * @note disabled for pointer types |
188 | | - * @note same signature as in the existing RealtimeBox<T> |
189 | | - */ |
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 | | - |
198 | | - /** |
199 | | - * @brief wait until the mutex could be locked and access the content (rw) |
200 | | - */ |
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 | | - |
213 | | - /** |
214 | | - * @brief Wait until the mutex could be locked and get the content (RealtimeBox behaviour) |
215 | | - * @return copy of the value |
216 | | - */ |
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 | | - |
224 | | - /** |
225 | | - * @brief Wait until the mutex could be locked and get the content (r) |
226 | | - * @note same signature as in the existing RealtimeBox<T> |
227 | | - */ |
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 | | - |
236 | | - /** |
237 | | - * @brief Wait until the mutex could be locked and access the content (r) |
238 | | - * @note only safe way to access pointer type content (r) |
239 | | - * @note same signature as in the existing RealtimeBox<T> |
240 | | - */ |
241 | | - void get(const std::function<void(const T &)> & func) |
242 | | - { |
243 | | - std::lock_guard<mutex_t> guard(lock_); |
244 | | - func(value_); |
245 | | - } |
246 | | - |
247 | | - /** |
248 | | - * @brief provide a custom assignment operator for easier usage |
249 | | - * @note only to be used from non-RT! |
250 | | - */ |
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 | | - |
257 | | - /** |
258 | | - * @brief provide a custom conversion operator |
259 | | - * @note Can only be used from non-RT! |
260 | | - */ |
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 | | - |
268 | | - /** |
269 | | - * @brief provide a custom conversion operator |
270 | | - * @note Can be used from non-RT and RT contexts |
271 | | - */ |
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 | | - |
285 | | -private: |
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 |
299 | | -template <typename T> |
300 | | -using RealtimeBoxStandard = RealtimeBoxBase<T, std::mutex>; |
301 | | - |
302 | | -template <typename T> |
303 | | -using RealtimeBoxRecursive = RealtimeBoxBase<T, std::recursive_mutex>; |
304 | | - |
305 | | -// This is the specialisation we recommend to use in the end |
306 | | -template <typename T> |
307 | | -using RealtimeBox = RealtimeBoxStandard<T>; |
| 46 | +using RealtimeBox = RealtimeThreadSafeBox<T>; |
308 | 47 |
|
309 | 48 | } // namespace realtime_tools |
310 | 49 |
|
|
0 commit comments