9 template<
class _Ty,
bool _Option = false,
class _Sty = u
int32_t>
10 struct ring_queue_spinlock
12 using value_type = _Ty;
13 using size_type = _Sty;
15 static constexpr
bool use_option = _Option;
16 using optional_type = std::conditional_t<use_option, std::optional<value_type>, value_type>;
18 ring_queue_spinlock(
size_t sz);
20 ring_queue_spinlock(
const ring_queue_spinlock&) =
delete;
21 ring_queue_spinlock(ring_queue_spinlock&&) =
default;
22 ring_queue_spinlock& operator =(
const ring_queue_spinlock&) =
delete;
23 ring_queue_spinlock& operator =(ring_queue_spinlock&&) =
default;
25 auto size() const noexcept->size_type;
26 auto capacity() const noexcept->size_type;
27 bool empty() const noexcept;
28 bool full() const noexcept;
30 bool try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>);
31 bool try_pop(value_type& value) noexcept(std::is_nothrow_move_assignable_v<value_type>);
33 using container_type = std::conditional_t<std::is_same_v<value_type,
bool>, std::unique_ptr<optional_type[]>, std::vector<optional_type>>;
34 container_type m_bufferPtr;
35 size_type m_bufferSize;
37 size_type m_writeIndex;
38 size_type m_readIndex;
39 mutable resumef::spinlock m_lock;
40 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
41 std::atomic<size_type> m_count;
44 auto nextIndex(size_type a_count)
const noexcept->size_type;
47 template<
class _Ty,
bool _Option,
class _Sty>
48 ring_queue_spinlock<_Ty, _Option, _Sty>::ring_queue_spinlock(
size_t sz)
49 : m_bufferSize(static_cast<size_type>(sz + 1))
52 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
56 if constexpr (std::is_same_v<value_type, bool>)
57 m_bufferPtr = container_type{
new optional_type[sz + 1] };
59 m_bufferPtr.resize(sz + 1);
61 assert(sz < (std::numeric_limits<size_type>::max)());
64 template<
class _Ty,
bool _Option,
class _Sty>
65 auto ring_queue_spinlock<_Ty, _Option, _Sty>::nextIndex(size_type a_count)
const noexcept->size_type
67 return static_cast<size_type
>((a_count + 1) % m_bufferSize);
70 template<
class _Ty,
bool _Option,
class _Sty>
71 auto ring_queue_spinlock<_Ty, _Option, _Sty>::size() const noexcept->size_type
73 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
74 return m_count.load(std::memory_order_acquire);
76 std::scoped_lock __guard(this->m_lock);
78 if (m_writeIndex >= m_readIndex)
79 return (m_writeIndex - m_readIndex);
81 return (m_bufferSize + m_writeIndex - m_readIndex);
82 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
85 template<
class _Ty,
bool _Option,
class _Sty>
86 auto ring_queue_spinlock<_Ty, _Option, _Sty>::capacity() const noexcept->size_type
88 return m_bufferSize - 1;
91 template<
class _Ty,
bool _Option,
class _Sty>
92 bool ring_queue_spinlock<_Ty, _Option, _Sty>::empty() const noexcept
94 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
95 return m_count.load(std::memory_order_acquire) == 0;
97 std::scoped_lock __guard(this->m_lock);
99 return m_writeIndex == m_readIndex;
100 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
103 template<
class _Ty,
bool _Option,
class _Sty>
104 bool ring_queue_spinlock<_Ty, _Option, _Sty>::full() const noexcept
106 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
107 return (m_count.load(std::memory_order_acquire) == (m_bufferSize - 1));
109 std::scoped_lock __guard(this->m_lock);
111 return nextIndex(m_writeIndex) == m_readIndex;
112 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
115 template<
class _Ty,
bool _Option,
class _Sty>
117 bool ring_queue_spinlock<_Ty, _Option, _Sty>::try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>)
119 std::scoped_lock __guard(this->m_lock);
121 auto nextWriteIndex = nextIndex(m_writeIndex);
122 if (nextWriteIndex == m_readIndex)
125 assert(m_writeIndex < m_bufferSize);
127 m_bufferPtr[m_writeIndex] = std::move(value);
128 m_writeIndex = nextWriteIndex;
130 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
131 m_count.fetch_add(1, std::memory_order_acq_rel);
136 template<
class _Ty,
bool _Option,
class _Sty>
137 bool ring_queue_spinlock<_Ty, _Option, _Sty>::try_pop(value_type& value) noexcept(std::is_nothrow_move_assignable_v<value_type>)
139 std::scoped_lock __guard(this->m_lock);
141 if (m_readIndex == m_writeIndex)
144 optional_type& ov = m_bufferPtr[m_readIndex];
145 if constexpr (use_option)
147 value = std::move(ov.value());
152 value = std::move(ov);
155 m_readIndex = nextIndex(m_readIndex);
157 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
158 m_count.fetch_sub(1, std::memory_order_acq_rel);