Version: 1.0
queue.h
Go to the documentation of this file.
1 // Extrapolated from https://vorbrodt.blog/2019/02/09/template-concepts-sort-of/
2 #pragma once
3 
4 #include <mutex>
5 #include <utility>
6 #include <type_traits>
7 #include "semaphore.h"
8 
9 template<typename T>
11 {
12 public:
13  blocking_queue(unsigned int size)
14  : m_size(size), m_pushIndex(0), m_popIndex(0), m_count(0),
15  m_data((T*)operator new(size * sizeof(T))),
16  m_openSlots(size), m_fullSlots(0) {}
17 
18  blocking_queue(const blocking_queue&) = delete;
22 
23  ~blocking_queue() noexcept
24  {
25  while (m_count--)
26  {
27  m_data[m_popIndex].~T();
28  m_popIndex = ++m_popIndex % m_size;
29  }
30  operator delete(m_data);
31  }
32 
33  template<typename Q = T>
34  typename std::enable_if<
35  std::is_copy_constructible<Q>::value &&
36  std::is_nothrow_copy_constructible<Q>::value, void>::type
37  push(const T& item) noexcept
38  {
39  m_openSlots.wait();
40  {
41  std::lock_guard<std::mutex> lock(m_cs);
42  new (m_data + m_pushIndex) T(item);
43  m_pushIndex = ++m_pushIndex % m_size;
44  ++m_count;
45  }
46  m_fullSlots.post();
47  }
48 
49  template<typename Q = T>
50  typename std::enable_if<
51  std::is_copy_constructible<Q>::value &&
52  !std::is_nothrow_copy_constructible<Q>::value, void>::type
53  push(const T& item)
54  {
55  m_openSlots.wait();
56  {
57  std::lock_guard<std::mutex> lock(m_cs);
58  try
59  {
60  new (m_data + m_pushIndex) T(item);
61  }
62  catch (...)
63  {
64  m_openSlots.post();
65  throw;
66  }
67  m_pushIndex = ++m_pushIndex % m_size;
68  ++m_count;
69  }
70  m_fullSlots.post();
71  }
72 
73  template<typename Q = T>
74  typename std::enable_if<
75  std::is_move_constructible<Q>::value &&
76  std::is_nothrow_move_constructible<Q>::value, void>::type
77  push(T&& item) noexcept
78  {
79  m_openSlots.wait();
80  {
81  std::lock_guard<std::mutex> lock(m_cs);
82  new (m_data + m_pushIndex) T(std::move(item));
83  m_pushIndex = ++m_pushIndex % m_size;
84  ++m_count;
85  }
86  m_fullSlots.post();
87  }
88 
89  template<typename Q = T>
90  typename std::enable_if<
91  std::is_move_constructible<Q>::value &&
92  !std::is_nothrow_move_constructible<Q>::value, void>::type
93  push(T&& item)
94  {
95  m_openSlots.wait();
96  {
97  std::lock_guard<std::mutex> lock(m_cs);
98  try
99  {
100  new (m_data + m_pushIndex) T(std::move(item));
101  }
102  catch (...)
103  {
104  m_openSlots.post();
105  throw;
106  }
107  m_pushIndex = ++m_pushIndex % m_size;
108  ++m_count;
109  }
110  m_fullSlots.post();
111  }
112 
113  template<typename Q = T>
114  typename std::enable_if<
115  !std::is_move_assignable<Q>::value &&
116  std::is_nothrow_copy_assignable<Q>::value, void>::type
117  pop(T& item) noexcept
118  {
119  m_fullSlots.wait();
120  {
121  std::lock_guard<std::mutex> lock(m_cs);
122  item = m_data[m_popIndex];
123  m_data[m_popIndex].~T();
124  m_popIndex = ++m_popIndex % m_size;
125  --m_count;
126  }
127  m_openSlots.post();
128  }
129 
130  template<typename Q = T>
131  typename std::enable_if<
132  !std::is_move_assignable<Q>::value &&
133  !std::is_nothrow_copy_assignable<Q>::value, void>::type
134  pop(T& item)
135  {
136  m_fullSlots.wait();
137  {
138  std::lock_guard<std::mutex> lock(m_cs);
139  try
140  {
141  item = m_data[m_popIndex];
142  }
143  catch (...)
144  {
145  m_fullSlots.post();
146  throw;
147  }
148  m_data[m_popIndex].~T();
149  m_popIndex = ++m_popIndex % m_size;
150  --m_count;
151  }
152  m_openSlots.post();
153  }
154 
155  template<typename Q = T>
156  typename std::enable_if<
157  std::is_move_assignable<Q>::value &&
158  std::is_nothrow_move_assignable<Q>::value, void>::type
159  pop(T& item) noexcept
160  {
161  m_fullSlots.wait();
162  {
163  std::lock_guard<std::mutex> lock(m_cs);
164  item = std::move(m_data[m_popIndex]);
165  m_data[m_popIndex].~T();
166  m_popIndex = ++m_popIndex % m_size;
167  --m_count;
168  }
169  m_openSlots.post();
170  }
171 
172  template<typename Q = T>
173  typename std::enable_if<
174  std::is_move_assignable<Q>::value &&
175  !std::is_nothrow_move_assignable<Q>::value, void>::type
176  pop(T& item)
177  {
178  m_fullSlots.wait();
179  {
180  std::lock_guard<std::mutex> lock(m_cs);
181  try
182  {
183  item = std::move(m_data[m_popIndex]);
184  }
185  catch (...)
186  {
187  m_fullSlots.post();
188  throw;
189  }
190  m_data[m_popIndex].~T();
191  m_popIndex = ++m_popIndex % m_size;
192  --m_count;
193  }
194  m_openSlots.post();
195  }
196 
197  T pop() // noexcept(std::is_nothrow_invocable_r<void, decltype(&blocking_queue<T>::pop<T>), T&>::value)
198  {
199  T item;
200  pop(item);
201  return item;
202  }
203 
204  bool empty() const noexcept
205  {
206  std::lock_guard<std::mutex> lock(m_cs);
207  return m_count == 0;
208  }
209 
210  bool size() const noexcept
211  {
212  std::lock_guard<std::mutex> lock(m_cs);
213  return m_count;
214  }
215 
216  unsigned int max_size() const noexcept
217  {
218  return m_size;
219  }
220 
221 private:
222  const unsigned int m_size;
223  unsigned int m_pushIndex;
224  unsigned int m_popIndex;
225  unsigned int m_count;
226  T* m_data;
227 
228  fast_semaphore m_openSlots;
229  fast_semaphore m_fullSlots;
230  std::mutex m_cs;
231 };
std::enable_if< std::is_move_constructible< Q >::value &&std::is_nothrow_move_constructible< Q >::value, void >::type push(T &&item) noexcept
Definition: queue.h:77
std::enable_if< !std::is_move_assignable< Q >::value &&!std::is_nothrow_copy_assignable< Q >::value, void >::type pop(T &item)
Definition: queue.h:134
~blocking_queue() noexcept
Definition: queue.h:23
unsigned int max_size() const noexcept
Definition: queue.h:216
bool empty() const noexcept
Definition: queue.h:204
std::enable_if< std::is_move_assignable< Q >::value &&!std::is_nothrow_move_assignable< Q >::value, void >::type pop(T &item)
Definition: queue.h:176
blocking_queue(blocking_queue &&)=delete
std::enable_if< !std::is_move_assignable< Q >::value &&std::is_nothrow_copy_assignable< Q >::value, void >::type pop(T &item) noexcept
Definition: queue.h:117
blocking_queue(unsigned int size)
Definition: queue.h:13
std::enable_if< std::is_copy_constructible< Q >::value &&!std::is_nothrow_copy_constructible< Q >::value, void >::type push(const T &item)
Definition: queue.h:53
blocking_queue(const blocking_queue &)=delete
blocking_queue & operator=(const blocking_queue &)=delete
bool size() const noexcept
Definition: queue.h:210
std::enable_if< std::is_copy_constructible< Q >::value &&std::is_nothrow_copy_constructible< Q >::value, void >::type push(const T &item) noexcept
Definition: queue.h:37
std::enable_if< std::is_move_constructible< Q >::value &&!std::is_nothrow_move_constructible< Q >::value, void >::type push(T &&item)
Definition: queue.h:93
std::enable_if< std::is_move_assignable< Q >::value &&std::is_nothrow_move_assignable< Q >::value, void >::type pop(T &item) noexcept
Definition: queue.h:159
void wait()
Definition: semaphore.h:53
void post()
Definition: semaphore.h:45