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 }