20#ifndef LIBE3_MPMC_QUEUE_HPP
21#define LIBE3_MPMC_QUEUE_HPP
59 while (capacity_ < min_capacity) capacity_ <<= 1;
60 mask_ = capacity_ - 1;
64 buffer_ =
static_cast<Slot*
>(
65 ::operator
new(
sizeof(Slot) * capacity_,
69 for (
size_t i = 0; i < capacity_; ++i) {
70 new (&buffer_[i]) Slot();
73 buffer_[i].sequence.store(i, std::memory_order_relaxed);
78 for (
size_t i = 0; i < capacity_; ++i) {
95 size_t pos = head_.load(std::memory_order_relaxed);
97 Slot& slot = buffer_[pos & mask_];
98 size_t seq = slot.sequence.load(std::memory_order_acquire);
99 intptr_t diff =
static_cast<intptr_t
>(seq)
100 -
static_cast<intptr_t
>(pos);
104 if (head_.compare_exchange_weak(pos, pos + 1,
105 std::memory_order_relaxed)) {
106 slot.data = std::move(data);
108 slot.sequence.store(pos + 1, std::memory_order_release);
112 }
else if (diff < 0) {
116 pos = head_.load(std::memory_order_relaxed);
135 size_t pos = tail_.load(std::memory_order_relaxed);
137 Slot& slot = buffer_[pos & mask_];
138 size_t seq = slot.sequence.load(std::memory_order_acquire);
139 intptr_t diff =
static_cast<intptr_t
>(seq)
140 -
static_cast<intptr_t
>(pos + 1);
144 if (tail_.compare_exchange_weak(pos, pos + 1,
145 std::memory_order_relaxed)) {
146 data = std::move(slot.data);
148 slot.sequence.store(pos + capacity_,
149 std::memory_order_release);
153 }
else if (diff < 0) {
157 pos = tail_.load(std::memory_order_relaxed);
169 size_t h = head_.load(std::memory_order_relaxed);
170 size_t t = tail_.load(std::memory_order_relaxed);
171 return h >= t ? h - t : 0;
178 size_t capacity() const noexcept {
return capacity_; }
182 std::atomic<size_t> sequence{0};
186 Slot* buffer_{
nullptr};
203#if defined(__x86_64__) || defined(__i386__)
204 __asm__
volatile(
"pause" :::
"memory");
205#elif defined(__aarch64__) || defined(__arm__)
206 __asm__
volatile(
"yield" :::
"memory");
MpmcQueue & operator=(MpmcQueue &&)=delete
bool empty_approx() const noexcept
Return true if the queue appears empty (approximate).
MpmcQueue & operator=(const MpmcQueue &)=delete
MpmcQueue(const MpmcQueue &)=delete
bool try_pop(T &data)
Try to dequeue an item (non-blocking).
size_t size_approx() const noexcept
Approximate number of items currently in the queue.
bool try_push(const T &data)
Try to enqueue an item (copy overload).
bool try_push(T &&data)
Try to enqueue an item (non-blocking, move semantics).
size_t capacity() const noexcept
Actual slot capacity (always a power of two).
MpmcQueue(size_t min_capacity)
Construct a queue with at least min_capacity slots.
MpmcQueue(MpmcQueue &&)=delete
void cpu_relax() noexcept
Emit a CPU-level pause/yield hint inside spin-wait loops.
static constexpr size_t CACHE_LINE_SIZE
Assumed cache-line size (bytes) used for alignment padding.