16#if !defined(CORIUM_CACHE_LINE_SIZE)
17#if defined(CORIUM_COMPACT_MEMORY) || defined(CORIUM_DISABLE_CACHE_ALIGNMENT)
18#define CORIUM_CACHE_LINE_SIZE alignof(std::max_align_t)
20#define CORIUM_CACHE_LINE_SIZE 64
32template <
typename T, std::
size_t Capacity>
34 static_assert((Capacity & (Capacity - 1)) == 0,
"Capacity must be a power of 2.");
36 struct alignas(CORIUM_CACHE_LINE_SIZE) Cell {
37 std::atomic<std::size_t> sequence;
38 alignas(
alignof(T)) std::byte storage[
sizeof(T)];
40 template <
typename... Args>
41 void construct(Args&&... args) {
42 new (
static_cast<void*
>(storage)) T(std::forward<Args>(args)...);
45 [[nodiscard]] T& value()
noexcept {
46 return *std::launder(
reinterpret_cast<T*
>(storage));
49 [[nodiscard]]
const T& value()
const noexcept {
50 return *std::launder(
reinterpret_cast<const T*
>(storage));
53 void destroy()
noexcept {
65 for (std::size_t i = 0; i < Capacity; ++i) {
66 _buffer[i].sequence.store(i, std::memory_order_relaxed);
68 _enqueuePos.store(0, std::memory_order_relaxed);
69 _dequeuePos.store(0, std::memory_order_relaxed);
74 while (tryPop(dummy)) {}
85 template <
typename... Args>
88 std::size_t pos = _enqueuePos.load(std::memory_order_relaxed);
91 cell = &_buffer[pos & Mask];
92 std::size_t seq = cell->sequence.load(std::memory_order_acquire);
93 intptr_t diff =
static_cast<intptr_t
>(seq) -
static_cast<intptr_t
>(pos);
96 if (_enqueuePos.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
99 }
else if (diff < 0) {
100 return {
false,
false};
102 pos = _enqueuePos.load(std::memory_order_relaxed);
106 cell->construct(std::forward<Args>(args)...);
107 cell->sequence.store(pos + 1, std::memory_order_release);
109 const bool wasEmpty = (pos == _dequeuePos.load(std::memory_order_relaxed));
110 return {
true, wasEmpty};
116 Cell* cell =
nullptr;
117 std::size_t pos = _dequeuePos.load(std::memory_order_relaxed);
119 cell = &_buffer[pos & Mask];
120 std::size_t seq = cell->sequence.load(std::memory_order_acquire);
121 intptr_t diff =
static_cast<intptr_t
>(seq) -
static_cast<intptr_t
>(pos + 1);
124 _dequeuePos.store(pos + 1, std::memory_order_relaxed);
125 result = std::move(cell->value());
127 cell->sequence.store(pos + Mask + 1, std::memory_order_release);
135 [[nodiscard]]
bool empty() const noexcept {
136 const std::size_t pos = _dequeuePos.load(std::memory_order_relaxed);
137 const Cell* cell = &_buffer[pos & Mask];
138 const std::size_t seq = cell->sequence.load(std::memory_order_acquire);
139 const intptr_t diff =
static_cast<intptr_t
>(seq) -
static_cast<intptr_t
>(pos + 1);
144 [[nodiscard]]
static constexpr std::size_t
capacity() noexcept {
149 static constexpr std::size_t Mask = Capacity - 1;
157 template <
typename T, std::
size_t Capacity>
#define CORIUM_CACHE_LINE_SIZE
Definition MpscRingBuffer.hpp:20
Lock-free Multiple-Producer, Single-Consumer (MPSC) RingBuffer. Implements Dmitry Vyukov's algorithm ...
Definition MpscRingBuffer.hpp:33
bool tryPop(T &result)
Pop an item from the queue (Single-Consumer only).
Definition MpscRingBuffer.hpp:115
~MpscRingBuffer()
Definition MpscRingBuffer.hpp:72
MpscRingBuffer(const MpscRingBuffer &)=delete
MpscRingBuffer & operator=(MpscRingBuffer &&)=delete
PushResult tryPush(Args &&... args)
Push an item into the queue (Multi-Producer thread safe).
Definition MpscRingBuffer.hpp:86
MpscRingBuffer()
Definition MpscRingBuffer.hpp:64
MpscRingBuffer(MpscRingBuffer &&)=delete
bool empty() const noexcept
Check if the queue is empty (approximate if concurrent producers are active).
Definition MpscRingBuffer.hpp:135
MpscRingBuffer & operator=(const MpscRingBuffer &)=delete
static constexpr std::size_t capacity() noexcept
Return fixed capacity of this ring buffer.
Definition MpscRingBuffer.hpp:144
Definition Application.hpp:16
Definition MpscRingBuffer.hpp:59
bool wasEmpty
Definition MpscRingBuffer.hpp:61
bool pushed
Definition MpscRingBuffer.hpp:60