Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace existing RealtimeBox implementation with RealtimeBoxBestEffort implementation #146

Merged
merged 47 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b02e501
restructured interface in oder to allow replacing the existing Realti…
Jan 14, 2024
613c55b
added some additional comments
Jan 14, 2024
93e1401
fix bug
Jan 14, 2024
fc2e4a2
fixed wrong return type in templated method
Jan 14, 2024
dce1458
added copyright notice
Jan 16, 2024
63c51a7
Merge branch 'master' into firesurfer/master
christophfroehlich Jan 22, 2024
eaedafe
Fixed issues due to merge with master
Jan 23, 2024
e666b93
Merge branch 'master' into master
christophfroehlich Jan 24, 2024
aa62239
Merge branch 'master' into master
christophfroehlich Jan 24, 2024
a22d150
fixed spelling mistake
Jan 25, 2024
65bca5d
Replaced existing RealtimeBox with RealtimeBoxBestEffort, merged the …
Jan 25, 2024
186b4aa
fixed ament_cpplint issues
Jan 25, 2024
a8b3f8a
fixed last remaining cpplint issue
Jan 25, 2024
69b224c
suppress cppcheck missingReturn
Jan 25, 2024
250fb85
Merge branch 'master' into master
christophfroehlich Jan 31, 2024
e8372d6
Merge branch 'master' into replace_rt_box
christophfroehlich Jan 31, 2024
220111a
try to resolve comments wherever possible
Feb 28, 2024
c2900a3
Should fix pre-commit formatting warning
firesurfer Mar 1, 2024
6499036
Merge branch 'master' into master
christophfroehlich Mar 3, 2024
4fce184
Implemented suggestions
Mar 22, 2024
d7a520c
Merge branch 'master' into master
firesurfer Mar 22, 2024
eaed073
Merge remote-tracking branch 'orig/master'
Apr 5, 2024
b679e9f
merged main into branch
Apr 5, 2024
ccd13a0
run precommit
Apr 5, 2024
9e0ce28
precommit should be satisfied now
Apr 5, 2024
e3c0d8a
provide specialisation for pointer with deprecation notice
Apr 5, 2024
d3869d2
Adapt copyright notice
Apr 6, 2024
e251b00
Merge branch 'master' into replace_rt_box
firesurfer Apr 25, 2024
85a421e
Update test/realtime_box_tests.cpp
destogl Oct 9, 2024
f97e97c
Merge branch 'master' into replace_rt_box
christophfroehlich Oct 30, 2024
02df04a
Add missing include
christophfroehlich Oct 30, 2024
50245c0
Merge remote-tracking branch 'rctrl/master'
Nov 10, 2024
5c7be4b
merge master
Nov 10, 2024
dcf97cd
removed deprecation overload for pointer types and addressed snake ca…
Nov 10, 2024
e22b575
added copy/move constructors
Nov 10, 2024
6ece6a4
Satisfy pre-commit
christophfroehlich Nov 10, 2024
1f63cac
Merge branch 'master' into replace_rt_box
christophfroehlich Nov 20, 2024
450e3d5
Update include/realtime_tools/realtime_box.h
firesurfer Nov 21, 2024
3a31a02
Update realtime_box.h
firesurfer Nov 21, 2024
0b61023
Re add comment about mutex
firesurfer Nov 21, 2024
9b9444d
Provide different specialisations for more convenience - remove stric…
Nov 21, 2024
16bd2ba
fix pre-commit
Nov 21, 2024
4138280
Fixed some happy little mistakes
Nov 21, 2024
d128cc3
Useful deprecation message
firesurfer Nov 21, 2024
0cb679d
precommit
Nov 21, 2024
bbe227a
Readd the old realtime_box author
saikishor Nov 21, 2024
3f57b78
Update include/realtime_tools/realtime_box.h
bmagyar Nov 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ if(BUILD_TESTING)
ament_add_gmock(realtime_box_tests test/realtime_box_tests.cpp)
target_link_libraries(realtime_box_tests realtime_tools)

ament_add_gmock(realtime_box_best_effort_tests test/realtime_box_best_effort_tests.cpp)
target_link_libraries(realtime_box_best_effort_tests realtime_tools)

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

Expand Down
251 changes: 226 additions & 25 deletions include/realtime_tools/realtime_box.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2009, Willow Garage, Inc.
// Copyright (c) 2024, Lennart Nachtigall
//
firesurfer marked this conversation as resolved.
Show resolved Hide resolved
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
Expand Down Expand Up @@ -26,50 +26,251 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// Author: Stuart Glaser
// Author: Lennart Nachtigall

saikishor marked this conversation as resolved.
Show resolved Hide resolved
#ifndef REALTIME_TOOLS__REALTIME_BOX_H__
#define REALTIME_TOOLS__REALTIME_BOX_H__
#ifndef REALTIME_TOOLS__REALTIME_BOX_H_
#define REALTIME_TOOLS__REALTIME_BOX_H_

#include <functional>
#include <initializer_list>
#include <mutex>
#include <string>
#include <optional>
#include <utility>

#include <rcpputils/pointer_traits.hpp>

namespace realtime_tools
{

template <typename T>
constexpr auto is_ptr_or_smart_ptr = rcpputils::is_pointer<T>::value;

/*!
A Box that ensures thread safe access to the boxed contents.
Access is best effort. If it can not lock it will return.

Strongly suggested that you use an std::shared_ptr in this box to
guarantee realtime safety.
NOTE about pointers:
You can use pointers with this box but the access will be different.
Only use the get/set methods that take function pointer for accessing the internal value.
*/

*/
template <class T>
// Provide a specialisation for non pointer types
// NOTE: When migrating to a safe access only version just remove the specialisation for pointer
// and let this be the only version!
template <class T, typename mutex_type = std::mutex>
class RealtimeBox
{
static_assert(
std::is_same_v<mutex_type, std::mutex> || std::is_same_v<mutex_type, std::recursive_mutex>);
static_assert(std::is_copy_constructible_v<T>, "Passed type must be copy constructible");
firesurfer marked this conversation as resolved.
Show resolved Hide resolved

public:
explicit RealtimeBox(const T & initial = T()) : thing_(initial) {}
using mutex_t = mutex_type;
using type = T;
// Provide various constructors
constexpr explicit RealtimeBox(const T & init = T{}) : value_(init) {}
constexpr explicit RealtimeBox(const T && init) : value_(std::move(init)) {}

// Copy constructor
constexpr RealtimeBox(const RealtimeBox & o)
{
// Lock the other box mutex
std::unique_lock<mutex_t> lock(o.lock_);
// We do not need to lock our own mutex because we are currently in the process of being created
value_ = o.value_;
}
// Copy assignment constructor
constexpr RealtimeBox & operator=(const RealtimeBox & o)
{
// Check for self assignment (and a potential deadlock)
if (&o != this) {
// Lock the other box mutex
std::unique_lock<mutex_t> lock_other(o.lock_);
std::unique_lock<mutex_t> lock_self(lock_);

value_ = o.value_;
}
return *this;
}
constexpr RealtimeBox(RealtimeBox && o)
{
// Lock the other box mutex
std::unique_lock<mutex_t> lock(o.lock_);
// We do not need to lock our own mutex because we are currently in the process of being created
value_ = std::move(o.value_);
}
// Only enabled for types that can be constructed from an initializer list
template <typename U = T>
constexpr RealtimeBox(
const std::initializer_list<U> & init,
std::enable_if_t<std::is_constructible_v<U, std::initializer_list>>)
: value_(init)
bmagyar marked this conversation as resolved.
Show resolved Hide resolved
{
}
constexpr RealtimeBox & operator=(RealtimeBox && o)
{
// Check for self assignment (and a potential deadlock)
if (&o != this) {
// Lock the other box mutex
std::unique_lock<mutex_t> lock_other(o.lock_);
std::unique_lock<mutex_t> lock_self(lock_);

value_ = std::move(o.value_);
}
return *this;
}

void set(const T & value)
/**
* @brief set a new content with best effort
* @return false if mutex could not be locked
* @note disabled for pointer types
*/
template <typename U = T>
typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, bool> try_set(const T & value)
{
std::lock_guard<std::mutex> guard(thing_lock_RT_);
thing_ = value;
std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
if (!guard.try_lock()) {
return false;
}
value_ = value;
return true;
}
/**
* @brief access the content readable with best effort
* @return false if the mutex could not be locked
* @note only safe way to access pointer type content (rw)
*/
bool try_set(const std::function<void(T &)> & func)
{
std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
if (!guard.try_lock()) {
return false;
}

void get(T & ref)
func(value_);
return true;
}
/**
* @brief get the content with best effort
* @return std::nullopt if content could not be access, otherwise the content is returned
*/
template <typename U = T>
[[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, std::optional<U>> try_get() const
{
std::lock_guard<std::mutex> guard(thing_lock_RT_);
ref = thing_;
std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
if (!guard.try_lock()) {
return std::nullopt;
}
return value_;
}
/**
* @brief access the content (r) with best effort
* @return false if the mutex could not be locked
* @note only safe way to access pointer type content (r)
*/
bool try_get(const std::function<void(const T &)> & func)
{
std::unique_lock<mutex_t> guard(lock_, std::defer_lock);
if (!guard.try_lock()) {
return false;
}

func(value_);
return true;
}

/**
* @brief set the content and wait until the mutex could be locked (RealtimeBox behavior)
* @return true
*/
template <typename U = T>
typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> set(const T & value)
{
std::lock_guard<mutex_t> guard(lock_);
// cppcheck-suppress missingReturn
value_ = value;
}
/**
* @brief access the content (rw) and wait until the mutex could locked
*/
void set(const std::function<void(T &)> & func)
{
std::lock_guard<mutex_t> guard(lock_);
func(value_);
}

/**
* @brief get the content and wait until the mutex could be locked (RealtimeBox behaviour)
* @return copy of the value
*/
template <typename U = T>
[[nodiscard]] typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, U> get() const
{
std::lock_guard<mutex_t> guard(lock_);
return value_;
}
/**
* @brief get the content and wait until the mutex could be locked
* @note same signature as in the existing RealtimeBox<T>
*/
template <typename U = T>
typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> get(T & in) const
{
std::lock_guard<mutex_t> guard(lock_);
// cppcheck-suppress missingReturn
in = value_;
}
/**
* @brief access the content (r) and wait until the mutex could be locked
* @note only safe way to access pointer type content (r)
* @note same signature as in the existing RealtimeBox<T>
*/
void get(const std::function<void(const T &)> & func)
{
std::lock_guard<mutex_t> guard(lock_);
func(value_);
}

/**
* @brief provide a custom assignment operator for easier usage
* @note only to be used from non-RT!
*/
template <typename U = T>
typename std::enable_if_t<!is_ptr_or_smart_ptr<U>, void> operator=(const T & value)
{
set(value);
}

/**
* @brief provide a custom conversion operator
* @note Can only be used from non-RT!
*/
template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
[[nodiscard]] operator T() const
{
// Only makes sense with the getNonRT method otherwise we would return an std::optional
return get();
}
/**
* @brief provide a custom conversion operator
* @note Can be used from non-RT and RT contexts
*/
template <typename U = T, typename = typename std::enable_if_t<!is_ptr_or_smart_ptr<U>>>
[[nodiscard]] operator std::optional<T>() const
{
return try_set();
}
firesurfer marked this conversation as resolved.
Show resolved Hide resolved

// In case one wants to actually use a pointer
// in this implementation we allow accessing the lock directly.
// Note: Be careful with lock.unlock().
// It may only be called from the thread that locked the mutex!
[[nodiscard]] const mutex_t & get_mutex() const { return lock_; }
[[nodiscard]] mutex_t & get_mutex() { return lock_; }

saikishor marked this conversation as resolved.
Show resolved Hide resolved
private:
// The thing that's in the box.
T thing_;

// Protects access to the thing in the box. This mutex is
// guaranteed to be locked for no longer than the duration of the
// copy, so as long as the copy is realtime safe and the OS has
// priority inheritance for mutexes, this lock can be safely locked
// from within realtime.
std::mutex thing_lock_RT_;
firesurfer marked this conversation as resolved.
Show resolved Hide resolved
T value_;
mutable mutex_t lock_;
};

} // namespace realtime_tools
Expand Down
Loading
Loading