ros2_control - rolling
Loading...
Searching...
No Matches
lock_free_queue.hpp
1// Copyright 2025 PAL Robotics S.L.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
17#ifndef REALTIME_TOOLS__LOCK_FREE_QUEUE_HPP_
18#define REALTIME_TOOLS__LOCK_FREE_QUEUE_HPP_
19
20#include <chrono>
21#include <mutex>
22#include <type_traits>
23#include <utility>
24
25#include <boost/lockfree/queue.hpp>
26#include <boost/lockfree/spsc_queue.hpp>
27#include <boost/lockfree/stack.hpp>
28
29namespace realtime_tools
30{
31namespace detail
32{
33// Trait to check if the capacity is set
34template <typename T>
35struct has_capacity : std::false_type
36{
37};
38
39template <typename T, std::size_t Capacity>
40struct has_capacity<boost::lockfree::spsc_queue<T, boost::lockfree::capacity<Capacity>>>
41: std::true_type
42{
43};
44
45template <typename T, std::size_t Capacity>
46struct has_capacity<boost::lockfree::queue<T, boost::lockfree::capacity<Capacity>>> : std::true_type
47{
48};
49
50template <typename T, std::size_t Capacity, bool FixedSize>
51struct has_capacity<boost::lockfree::queue<
52 T, boost::lockfree::capacity<Capacity>, boost::lockfree::fixed_sized<FixedSize>>> : std::true_type
53{
54};
55
56// Traits to check if the queue is spsc_queue
57template <typename T>
58struct is_spsc_queue : std::false_type
59{
60};
61
62template <typename T>
63struct is_spsc_queue<boost::lockfree::spsc_queue<T>> : std::true_type
64{
65};
66
67template <typename T, std::size_t Capacity>
68struct is_spsc_queue<boost::lockfree::spsc_queue<T, boost::lockfree::capacity<Capacity>>>
69: std::true_type
70{
71};
72
73// Traits to get the capacity of the queue
74// Default case: no capacity
75template <typename T>
77{
78 static constexpr std::size_t value = 0; // Default to 0 if capacity is not defined
79};
80
81// Specialization for queues with capacity
82template <typename T, std::size_t Capacity>
84 boost::lockfree::spsc_queue<T, boost::lockfree::capacity<Capacity>>>
85{
86 static constexpr std::size_t value = Capacity;
87};
88
89// Specialization for queues with capacity
90template <typename T, std::size_t Capacity>
92 boost::lockfree::queue<T, boost::lockfree::capacity<Capacity>>>
93{
94 static constexpr std::size_t value = Capacity;
95};
96
97// Specialization for queues with capacity
98template <typename T, std::size_t Capacity, bool FixedSize>
99struct get_boost_lockfree_queue_capacity<boost::lockfree::queue<
100 T, boost::lockfree::capacity<Capacity>, boost::lockfree::fixed_sized<FixedSize>>>
101{
102 static constexpr std::size_t value = Capacity;
103};
104
105} // namespace detail
106
117template <typename DataType, typename LockFreeContainer>
119{
120public:
121 using T = DataType;
122
127 template <
129 typename std::enable_if_t<HasCapacity, int> = 0>
131 : capacity_(detail::get_boost_lockfree_queue_capacity<LockFreeContainer>::value)
132 {
133 }
134
140 template <
142 typename std::enable_if_t<!HasCapacity, int> = 1>
143 explicit LockFreeQueueBase(std::size_t capacity) : data_queue_(capacity), capacity_(capacity)
144 {
145 }
146
153 [[nodiscard]] bool pop(T & data) { return data_queue_.pop(data); }
154
162 template <typename U>
163 [[nodiscard]] std::enable_if_t<std::is_convertible_v<T, U>, bool> pop(U & data)
164 {
165 return data_queue_.pop(data);
166 }
167
175 [[nodiscard]] bool push(const T & data)
176 {
178 return data_queue_.push(data);
179 } else {
180 return data_queue_.bounded_push(data);
181 }
182 }
183
191 template <typename U>
192 [[nodiscard]] std::enable_if_t<std::is_convertible_v<const U &, T>, bool> push(const U & data)
193 {
195 return data_queue_.push(data);
196 } else {
197 return data_queue_.bounded_push(data);
198 }
199 }
200
221 template <typename U>
222 [[nodiscard]] std::enable_if_t<std::is_convertible_v<const U &, T>, bool> bounded_push(
223 const U & data)
224 {
225 if (!push(data)) {
226 T dummy;
227 data_queue_.pop(dummy);
228 return push(data);
229 }
230 return true;
231 }
232
245 [[nodiscard]] bool get_latest(T & data)
246 {
248 // Snapshot the count so the work is bounded even if the producer keeps pushing meanwhile.
249 // The snapshot is a lower bound (the producer only adds), so the last element of the
250 // snapshot is still there to be popped below.
251 const std::size_t available = data_queue_.read_available();
252 for (std::size_t i = 1; i < available; ++i) {
253 data_queue_.consume_one([](const T &) {});
254 }
255 return data_queue_.pop(data);
256 } else {
257 return data_queue_.consume_all([&data](const T & d) { data = d; }) > 0;
258 }
259 }
260
268 bool empty() const
269 {
271 return data_queue_.read_available() == 0;
272 } else {
273 return data_queue_.empty();
274 }
275 }
276
281 size_t capacity() const { return capacity_; }
282
288 template <
290 typename std::enable_if_t<IsSPSCQueue, int> = 0>
291 std::size_t size() const
292 {
293 return data_queue_.read_available();
294 }
295
307 bool is_lock_free() const
308 {
309 return (detail::is_spsc_queue<LockFreeContainer>::value) || data_queue_.is_lock_free();
310 }
311
316 const LockFreeContainer & get_lockfree_container() const { return data_queue_; }
317
322 LockFreeContainer & get_lockfree_container() { return data_queue_; }
323
324private:
325 LockFreeContainer data_queue_;
326 std::size_t capacity_;
327}; // class
328
334template <class DataType, std::size_t Capacity = 0>
335using LockFreeSPSCQueue = std::conditional_t<
338 DataType, boost::lockfree::spsc_queue<DataType, boost::lockfree::capacity<Capacity>>>>;
339
346template <class DataType, std::size_t Capacity = 0, bool FixedSize = true>
347using LockFreeMPMCQueue = std::conditional_t<
348 Capacity == 0,
350 DataType, boost::lockfree::queue<DataType, boost::lockfree::fixed_sized<FixedSize>>>,
352 DataType,
353 boost::lockfree::queue<
354 DataType, boost::lockfree::capacity<Capacity>, boost::lockfree::fixed_sized<FixedSize>>>>;
355
356} // namespace realtime_tools
357#endif // REALTIME_TOOLS__LOCK_FREE_QUEUE_HPP_
A base class for lock-free queues.
Definition lock_free_queue.hpp:119
std::enable_if_t< std::is_convertible_v< const U &, T >, bool > bounded_push(const U &data)
The bounded_push function pushes the data into the queue and pops the oldest data if the queue is ful...
Definition lock_free_queue.hpp:222
LockFreeContainer & get_lockfree_container()
Get the lockfree container.
Definition lock_free_queue.hpp:322
bool get_latest(T &data)
Get the latest data from the queue.
Definition lock_free_queue.hpp:245
std::enable_if_t< std::is_convertible_v< T, U >, bool > pop(U &data)
Pop the data from the queue.
Definition lock_free_queue.hpp:163
LockFreeQueueBase()
Construct a new LockFreeQueueBase object.
Definition lock_free_queue.hpp:130
bool is_lock_free() const
The method to check if the queue is lock free.
Definition lock_free_queue.hpp:307
std::enable_if_t< std::is_convertible_v< const U &, T >, bool > push(const U &data)
Push the data into the queue.
Definition lock_free_queue.hpp:192
bool empty() const
Check if the queue is empty.
Definition lock_free_queue.hpp:268
size_t capacity() const
Get the capacity of the queue.
Definition lock_free_queue.hpp:281
bool push(const T &data)
Push the data into the queue.
Definition lock_free_queue.hpp:175
std::size_t size() const
Get the size of the queue.
Definition lock_free_queue.hpp:291
bool pop(T &data)
Pop the data from the queue.
Definition lock_free_queue.hpp:153
LockFreeQueueBase(std::size_t capacity)
Construct a new LockFreeQueueBase object.
Definition lock_free_queue.hpp:143
const LockFreeContainer & get_lockfree_container() const
Get the lockfree container.
Definition lock_free_queue.hpp:316
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:42
std::conditional_t< Capacity==0, LockFreeQueueBase< DataType, boost::lockfree::spsc_queue< DataType > >, LockFreeQueueBase< DataType, boost::lockfree::spsc_queue< DataType, boost::lockfree::capacity< Capacity > > > > LockFreeSPSCQueue
Lock-free Single Producer Single Consumer Queue.
Definition lock_free_queue.hpp:338
std::conditional_t< Capacity==0, LockFreeQueueBase< DataType, boost::lockfree::queue< DataType, boost::lockfree::fixed_sized< FixedSize > > >, LockFreeQueueBase< DataType, boost::lockfree::queue< DataType, boost::lockfree::capacity< Capacity >, boost::lockfree::fixed_sized< FixedSize > > > > LockFreeMPMCQueue
Lock-free Multiple Producer Multiple Consumer Queue.
Definition lock_free_queue.hpp:354
Definition lock_free_queue.hpp:36
Definition lock_free_queue.hpp:59