Skip to content

Commit 3f46a8d

Browse files
Rename RealtimeBox to RealtimeThreadsafeBox and use prio_inherit_mutex (#318)
1 parent 6a7d39b commit 3f46a8d

File tree

4 files changed

+364
-308
lines changed

4 files changed

+364
-308
lines changed

realtime_tools/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ if(BUILD_TESTING)
7373
ament_add_gmock(thread_priority_tests test/thread_priority_tests.cpp)
7474
target_link_libraries(thread_priority_tests thread_priority)
7575

76-
ament_add_gmock(realtime_box_tests test/realtime_box_tests.cpp)
77-
target_link_libraries(realtime_box_tests realtime_tools)
76+
ament_add_gmock(realtime_thread_safe_box_tests test/realtime_thread_safe_box_tests.cpp)
77+
target_link_libraries(realtime_thread_safe_box_tests realtime_tools)
7878

7979
ament_add_gmock(realtime_buffer_tests test/realtime_buffer_tests.cpp)
8080
target_link_libraries(realtime_buffer_tests realtime_tools)

realtime_tools/include/realtime_tools/realtime_box.hpp

Lines changed: 5 additions & 266 deletions
Original file line numberDiff line numberDiff line change
@@ -33,278 +33,17 @@
3333
#ifndef REALTIME_TOOLS__REALTIME_BOX_HPP_
3434
#define REALTIME_TOOLS__REALTIME_BOX_HPP_
3535

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"
4137

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
4341

4442
namespace realtime_tools
4543
{
4644

4745
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>;
30847

30948
} // namespace realtime_tools
31049

0 commit comments

Comments
 (0)