14NC_DISABLE_WARNING_PUSH
15NC_DISABLE_WARNING_MSVC(4324)
18template <class T,
size_t N>
22 [[nodiscard]]
auto push(
const T& v) ->
bool
27 [[nodiscard]]
auto push(T&& v) ->
bool
29 return emplace(std::move(v));
32 [[nodiscard]]
auto pop(T& out) ->
bool
34 if (is_empty(m_consumer))
36 refresh_consumer_cache();
37 if (is_empty(m_consumer))
43 out = std::move(m_buffer[index(m_consumer.head)]);
48 template<
class... Args>
49 [[nodiscard]]
auto emplace(Args&&... args) ->
bool
51 if (is_full(m_producer))
53 refresh_producer_cache();
54 if (is_full(m_producer))
60 m_buffer[index(m_producer.tail)] = T(std::forward<Args>(args)...);
65 [[nodiscard]]
auto empty()
const noexcept ->
bool
67 return m_head.load(std::memory_order_acquire) ==
68 m_tail.load(std::memory_order_acquire);
78 static_assert(N >= 1,
"Queue capacity must be >= 1");
79 static_assert(std::atomic<size_t>::is_always_lock_free);
80 static constexpr auto CacheLine = nc::hardware_destructive_interference_size;
82 alignas(CacheLine) std::array<T, N> m_buffer = {};
83 alignas(CacheLine) std::atomic<size_t> m_head = 0;
84 alignas(CacheLine) std::atomic<size_t> m_tail = 0;
85 alignas(CacheLine) Cache m_producer;
86 alignas(CacheLine) Cache m_consumer;
88 static constexpr auto index(
size_t i) ->
size_t
90 if constexpr ((N & (N - 1)) == 0)
100 static auto is_full(
const Cache& cache) ->
bool {
return cache.tail - cache.head >= N; }
101 static auto is_empty(
const Cache& cache) ->
bool {
return cache.head == cache.tail; }
102 void refresh_producer_cache() { m_producer.head = m_head.load(std::memory_order_acquire); }
103 void refresh_consumer_cache() { m_consumer.tail = m_tail.load(std::memory_order_acquire); }
104 void advance_producer() { m_tail.store(++m_producer.tail, std::memory_order_release); }
105 void advance_consumer() { m_head.store(++m_consumer.head, std::memory_order_release); }
108NC_DISABLE_WARNING_POP
Fixed-size single-producer/single-consumer queue.
Definition: SpscQueue.h:20