2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_DETAIL_MPSCRINGANY_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_DETAIL_MPSCRINGANY_HPP_INCLUDED
13namespace logit {
namespace detail {
21 static_assert(std::is_nothrow_move_constructible<T>::value &&
22 std::is_nothrow_move_assignable<T>::value &&
23 std::is_nothrow_destructible<T>::value,
24 "MpscRingAny requires no-throw move and destruction operations");
30 typename std::aligned_storage<
sizeof(T),
alignof(T)>::type
m_storage;
40 for (std::size_t i = 0; i <
m_cap; ++i) {
41 m_cells[i].m_seq.store(i, std::memory_order_relaxed);
56 m_cells(std::move(other.m_cells)),
57 m_enqueue_pos(other.m_enqueue_pos.load(std::memory_order_relaxed)),
58 m_dequeue_pos(other.m_dequeue_pos.load(std::memory_order_relaxed)) {
60 other.m_enqueue_pos.store(0, std::memory_order_relaxed);
61 other.m_dequeue_pos.store(0, std::memory_order_relaxed);
69 m_cells = std::move(other.m_cells);
70 m_enqueue_pos.store(other.m_enqueue_pos.load(std::memory_order_relaxed),
71 std::memory_order_relaxed);
72 m_dequeue_pos.store(other.m_dequeue_pos.load(std::memory_order_relaxed),
73 std::memory_order_relaxed);
75 other.m_enqueue_pos.store(0, std::memory_order_relaxed);
76 other.m_dequeue_pos.store(0, std::memory_order_relaxed);
88 static_assert(std::is_nothrow_constructible<T, U&&>::value,
89 "MpscRingAny::try_push requires no-throw construction; pass a movable value");
90 std::size_t pos =
m_enqueue_pos.load(std::memory_order_relaxed);
93 std::size_t seq = c.
m_seq.load(std::memory_order_acquire);
97 static_cast<std::intptr_t
>(seq) -
static_cast<std::intptr_t
>(pos);
102 std::memory_order_relaxed,
103 std::memory_order_relaxed)) {
105 new (&c.
m_storage) T(std::forward<U>(v));
107 c.
m_seq.store(pos + 1, std::memory_order_release);
111 }
else if (diff < 0) {
125 std::size_t pos =
m_dequeue_pos.load(std::memory_order_relaxed);
127 std::size_t seq = c.
m_seq.load(std::memory_order_acquire);
131 static_cast<std::intptr_t
>(seq) -
static_cast<std::intptr_t
>(pos + 1);
136 std::memory_order_relaxed,
137 std::memory_order_relaxed)) {
142 T* p =
reinterpret_cast<T*
>(&c.
m_storage);
147 c.
m_seq.store(pos +
m_cap, std::memory_order_release);
160 std::size_t pos =
m_dequeue_pos.load(std::memory_order_acquire);
162 std::size_t seq = c.
m_seq.load(std::memory_order_acquire);
164 static_cast<std::intptr_t
>(seq) -
static_cast<std::intptr_t
>(pos + 1);
178 const std::size_t begin =
m_dequeue_pos.load(std::memory_order_relaxed);
179 const std::size_t end =
m_enqueue_pos.load(std::memory_order_relaxed);
180 for (std::size_t pos = begin; pos != end; ++pos) {
182 const std::size_t seq = c.
m_seq.load(std::memory_order_relaxed);
183 if (seq == pos + 1) {
184 reinterpret_cast<T*
>(&c.
m_storage)->~T();
Bounded MPSC ring buffer with arbitrary capacity (C++11).
MpscRingAny & operator=(MpscRingAny &&other) noexcept
Move assign ring, draining existing payload.
bool empty() const noexcept
Lightweight emptiness check for current consumer position.
void destroy_all_() noexcept
std::atomic< std::size_t > m_enqueue_pos
bool try_pop(T &out) noexcept
Try to dequeue value into out.
bool try_push(U &&v) noexcept
Try to enqueue value.
MpscRingAny(const MpscRingAny &)=delete
std::atomic< std::size_t > m_dequeue_pos
MpscRingAny & operator=(const MpscRingAny &)=delete
std::unique_ptr< Cell[]> m_cells
MpscRingAny(std::size_t capacity)
Construct ring with given capacity (>= 2).
~MpscRingAny()
Destroy remaining elements, if any.
MpscRingAny(MpscRingAny &&other) noexcept
Move construct ring, adopting producer/consumer indices.
std::size_t capacity() const noexcept
Capacity of the ring.
The primary namespace for the LogIt++ library.
Single cell storing sequence number and raw storage for T.
std::aligned_storage< sizeof(T), alignof(T)>::type m_storage
std::atomic< std::size_t > m_seq