librf

ring_queue.h
1 #pragma once
2
3 namespace resumef
4 {
5 //使用自旋锁完成的线程安全的环形队列。
6 //支持多个线程同时push和pop。
7 //_Option : 如果队列保存的数据不支持拷贝只支持移动，则需要设置为true；或者数据希望pop后销毁，都需要设置为true。
8 //_Sty : 内存保持数量和索引的整数类型。用于外部控制队列的结构体大小。
9 template<class _Ty, bool _Option = false, class _Sty = uint32_t>
10 struct ring_queue
11 {
12 using value_type = _Ty;
13 using size_type = _Sty;
14
15 static constexpr bool use_option = _Option;
16 using optional_type = std::conditional_t<use_option, std::optional<value_type>, value_type>;
17 public:
18 ring_queue(size_t sz);
19
20 ring_queue(const ring_queue&) = delete;
21 ring_queue(ring_queue&&) = default;
22 ring_queue& operator =(const ring_queue&) = delete;
23 ring_queue& operator =(ring_queue&&) = default;
24
25 auto size() const noexcept->size_type;
26 auto capacity() const noexcept->size_type;
27 bool empty() const noexcept;
28 bool full() const noexcept;
29 template<class U>
30 bool try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>);
31 template<class U>
32 bool try_pop(U& value) noexcept(std::is_nothrow_move_assignable_v<value_type>);
33 private:
34 using container_type = std::unique_ptr<optional_type[]>;
35 container_type m_bufferPtr;
36 size_type m_bufferSize;
37
38 size_type m_writeIndex;
39 size_type m_readIndex;
40 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
41 std::atomic<size_type> m_count;
42 #endif
43
44 auto nextIndex(size_type a_count) const noexcept->size_type;
45 };
46
47 template<class _Ty, bool _Option, class _Sty>
48 ring_queue<_Ty, _Option, _Sty>::ring_queue(size_t sz)
49 : m_bufferPtr(new optional_type[sz + 1])
50 , m_bufferSize(static_cast<size_type>(sz + 1))
51 , m_writeIndex(0)
52 , m_readIndex(0)
53 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
54 , m_count(0)
55 #endif
56 {
57 assert(sz < (std::numeric_limits<size_type>::max)());
58 }
59
60 template<class _Ty, bool _Option, class _Sty>
61 auto ring_queue<_Ty, _Option, _Sty>::nextIndex(size_type a_count) const noexcept->size_type
62 {
63 return static_cast<size_type>((a_count + 1) % m_bufferSize);
64 }
65
66 template<class _Ty, bool _Option, class _Sty>
67 auto ring_queue<_Ty, _Option, _Sty>::size() const noexcept->size_type
68 {
69 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
70 return m_count.load(std::memory_order_acquire);
71 #else
72 if (m_writeIndex >= m_readIndex)
73 return (m_writeIndex - m_readIndex);
74 else
75 return (m_bufferSize + m_writeIndex - m_readIndex);
76 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
77 }
78
79 template<class _Ty, bool _Option, class _Sty>
80 auto ring_queue<_Ty, _Option, _Sty>::capacity() const noexcept->size_type
81 {
82 return m_bufferSize - 1;
83 }
84
85 template<class _Ty, bool _Option, class _Sty>
86 bool ring_queue<_Ty, _Option, _Sty>::empty() const noexcept
87 {
88 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
89 return m_count.load(std::memory_order_acquire) == 0;
90 #else
91 return m_writeIndex == m_readIndex;
92 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
93 }
94
95 template<class _Ty, bool _Option, class _Sty>
96 bool ring_queue<_Ty, _Option, _Sty>::full() const noexcept
97 {
98 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
99 return (m_count.load(std::memory_order_acquire) == (m_bufferSize - 1));
100 #else
101 return nextIndex(m_writeIndex) == m_readIndex;
102 #endif // _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
103 }
104
105 template<class _Ty, bool _Option, class _Sty>
106 template<class U>
107 bool ring_queue<_Ty, _Option, _Sty>::try_push(U&& value) noexcept(std::is_nothrow_move_assignable_v<U>)
108 {
109 auto nextWriteIndex = nextIndex(m_writeIndex);
110 if (nextWriteIndex == m_readIndex)
111 return false;
112
113 assert(m_writeIndex < m_bufferSize);
114
115 m_bufferPtr[m_writeIndex] = std::move(value);
116 m_writeIndex = nextWriteIndex;
117
118 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
119 m_count.fetch_add(1, std::memory_order_acq_rel);
120 #endif
121 return true;
122 }
123
124 template<class _Ty, bool _Option, class _Sty>
125 template<class U>
126 bool ring_queue<_Ty, _Option, _Sty>::try_pop(U& value) noexcept(std::is_nothrow_move_assignable_v<value_type>)
127 {
128 if (m_readIndex == m_writeIndex)
129 return false;
130
131 optional_type& ov = m_bufferPtr[m_readIndex];
132 if constexpr (use_option)
133 {
134 value = std::move(ov).value();
135 ov = std::nullopt;
136 }
137 else
138 {
139 value = std::move(ov);
140 }
141
142 m_readIndex = nextIndex(m_readIndex);
143
144 #ifdef _WITH_LOCK_FREE_Q_KEEP_REAL_SIZE
145 m_count.fetch_sub(1, std::memory_order_acq_rel);
146 #endif
147 return true;
148 }
149 }