256 lines
4.8 KiB

2 years ago
  1. #pragma once
  2. #include <mutex>
  3. #include <queue>
  4. #include <utility>
  5. #include <stdexcept>
  6. #include <condition_variable>
  7. template<typename T>
  8. class unbounded_queue
  9. {
  10. public:
  11. explicit unbounded_queue(bool block = true)
  12. : m_block{ block } {}
  13. void push(const T& item)
  14. {
  15. {
  16. std::scoped_lock guard(m_queue_lock);
  17. m_queue.push(item);
  18. }
  19. m_condition.notify_one();
  20. }
  21. void push(T&& item)
  22. {
  23. {
  24. std::scoped_lock guard(m_queue_lock);
  25. m_queue.push(std::move(item));
  26. }
  27. m_condition.notify_one();
  28. }
  29. template<typename... Args>
  30. void emplace(Args&&... args)
  31. {
  32. {
  33. std::scoped_lock guard(m_queue_lock);
  34. m_queue.emplace(std::forward<Args>(args)...);
  35. }
  36. m_condition.notify_one();
  37. }
  38. bool try_push(const T& item)
  39. {
  40. {
  41. std::unique_lock lock(m_queue_lock, std::try_to_lock);
  42. if(!lock)
  43. return false;
  44. m_queue.push(item);
  45. }
  46. m_condition.notify_one();
  47. return true;
  48. }
  49. bool try_push(T&& item)
  50. {
  51. {
  52. std::unique_lock lock(m_queue_lock, std::try_to_lock);
  53. if(!lock)
  54. return false;
  55. m_queue.push(std::move(item));
  56. }
  57. m_condition.notify_one();
  58. return true;
  59. }
  60. bool pop(T& item)
  61. {
  62. std::unique_lock guard(m_queue_lock);
  63. m_condition.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
  64. if(m_queue.empty())
  65. return false;
  66. item = std::move(m_queue.front());
  67. m_queue.pop();
  68. return true;
  69. }
  70. bool try_pop(T& item)
  71. {
  72. std::unique_lock lock(m_queue_lock, std::try_to_lock);
  73. if(!lock || m_queue.empty())
  74. return false;
  75. item = std::move(m_queue.front());
  76. m_queue.pop();
  77. return true;
  78. }
  79. std::size_t size() const
  80. {
  81. std::scoped_lock guard(m_queue_lock);
  82. return m_queue.size();
  83. }
  84. bool empty() const
  85. {
  86. std::scoped_lock guard(m_queue_lock);
  87. return m_queue.empty();
  88. }
  89. void block()
  90. {
  91. std::scoped_lock guard(m_queue_lock);
  92. m_block = true;
  93. }
  94. void unblock()
  95. {
  96. {
  97. std::scoped_lock guard(m_queue_lock);
  98. m_block = false;
  99. }
  100. m_condition.notify_all();
  101. }
  102. bool blocking() const
  103. {
  104. std::scoped_lock guard(m_queue_lock);
  105. return m_block;
  106. }
  107. private:
  108. using queue_t = std::queue<T>;
  109. queue_t m_queue;
  110. bool m_block;
  111. mutable std::mutex m_queue_lock;
  112. std::condition_variable m_condition;
  113. };
  114. template<typename T>
  115. class bounded_queue
  116. {
  117. public:
  118. explicit bounded_queue(std::size_t max_size, bool block = true)
  119. : m_block{ block }, m_max_size{ max_size }
  120. {
  121. if(!m_max_size)
  122. throw std::invalid_argument("bad queue max-size! must be non-zero!");
  123. }
  124. bool push(const T& item)
  125. {
  126. {
  127. std::unique_lock guard(m_queue_lock);
  128. m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
  129. if(m_queue.size() == m_max_size)
  130. return false;
  131. m_queue.push(item);
  132. }
  133. m_condition_pop.notify_one();
  134. return true;
  135. }
  136. bool push(T&& item)
  137. {
  138. {
  139. std::unique_lock guard(m_queue_lock);
  140. m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
  141. if(m_queue.size() == m_max_size)
  142. return false;
  143. m_queue.push(std::move(item));
  144. }
  145. m_condition_pop.notify_one();
  146. return true;
  147. }
  148. template<typename... Args>
  149. bool emplace(Args&&... args)
  150. {
  151. {
  152. std::unique_lock guard(m_queue_lock);
  153. m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
  154. if(m_queue.size() == m_max_size)
  155. return false;
  156. m_queue.emplace(std::forward<Args>(args)...);
  157. }
  158. m_condition_pop.notify_one();
  159. return true;
  160. }
  161. bool pop(T& item)
  162. {
  163. {
  164. std::unique_lock guard(m_queue_lock);
  165. m_condition_pop.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
  166. if(m_queue.empty())
  167. return false;
  168. item = std::move(m_queue.front());
  169. m_queue.pop();
  170. }
  171. m_condition_push.notify_one();
  172. return true;
  173. }
  174. std::size_t size() const
  175. {
  176. std::scoped_lock guard(m_queue_lock);
  177. return m_queue.size();
  178. }
  179. std::size_t capacity() const
  180. {
  181. return m_max_size;
  182. }
  183. bool empty() const
  184. {
  185. std::scoped_lock guard(m_queue_lock);
  186. return m_queue.empty();
  187. }
  188. bool full() const
  189. {
  190. std::scoped_lock lock(m_queue_lock);
  191. return m_queue.size() == capacity();
  192. }
  193. void block()
  194. {
  195. std::scoped_lock guard(m_queue_lock);
  196. m_block = true;
  197. }
  198. void unblock()
  199. {
  200. {
  201. std::scoped_lock guard(m_queue_lock);
  202. m_block = false;
  203. }
  204. m_condition_push.notify_all();
  205. m_condition_pop.notify_all();
  206. }
  207. bool blocking() const
  208. {
  209. std::scoped_lock guard(m_queue_lock);
  210. return m_block;
  211. }
  212. private:
  213. using queue_t = std::queue<T>;
  214. queue_t m_queue;
  215. bool m_block;
  216. const std::size_t m_max_size;
  217. mutable std::mutex m_queue_lock;
  218. std::condition_variable m_condition_push;
  219. std::condition_variable m_condition_pop;
  220. };