Queue  1.0.0
A C++17 Library of various `queue` containers
concurrent_queue.h
Go to the documentation of this file.
1 //--------------------------------------------------------------------------------------------------
2 //
3 // QUEUE
4 //
5 //--------------------------------------------------------------------------------------------------
6 //
7 // The MIT License (MIT)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software
10 // and associated documentation files (the "Software"), to deal in the Software without
11 // restriction, including without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all copies or
16 // substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
19 // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //--------------------------------------------------------------------------------------------------
25 //
26 // Copyright (c) 2015 Nic Holthaus
27 //
28 //--------------------------------------------------------------------------------------------------
29 //
30 // ATTRIBUTION:
31 // - https://en.cppreference.com/w/cpp/container/deque
32 // - https://stackoverflow.com/questions/29986208/how-should-i-deal-with-mutexes-in-movable-types-in-c/29988626
33 //
34 //--------------------------------------------------------------------------------------------------
35 //
36 // TODO:
37 // - Write a `concept` for what constitutes a queue type
38 // - `pop` should use an upgradeable mutex, if the C++ standard committee ever creates one.
39 //
40 //--------------------------------------------------------------------------------------------------
41 //
42 /// @file concurrent_queue.h
43 /// @brief A thread-safe queue (FIFO) implementation
44 //
45 //--------------------------------------------------------------------------------------------------
46 
47 #pragma once
48 #ifndef concurrent_queue_h__
49 #define concurrent_queue_h__
50 
51 //----------------------------
52 // INCLUDES
53 //----------------------------
54 
55 #include <chrono>
56 #include <condition_variable>
57 #include <deque>
58 #include <memory>
59 #include <mutex>
60 #include <shared_mutex>
61 
62 //-------------------------
63 // FORWARD DECLARATIONS
64 //-------------------------
65 
66 //----------------------------------------------------------------------------------------------------------------------
67 // CLASS: concurrent_queue
68 //----------------------------------------------------------------------------------------------------------------------
69 /// @brief The `concurrent_queue` class is a sequence container class that allows first-in, first-out access to its elements.
70 /// @details It enables a limited set of concurrency-safe operations, such as push and try_pop. Here, concurrency-safe means pointers
71 /// or iterators are always valid. It's not a guarantee of element initialization, or of a particular traversal order.
72 /// @tparam T The data type of the elements to be stored in the queue.
73 /// @tparam Queue Underlying concurrent queue data structure. Defaults to `std::deque`.
74 /// @tparam AllocThe type that represents the stored allocator object that encapsulates details about the allocation and deallocation
75 /// of memory for this concurrent queue. This argument is optional and the default value is allocator<T>.
76 template<class T, template<class, class> class Queue = std::deque, class Alloc = std::allocator<T>>
78 {
79 public:
80  //----------------------------
81  // TYPEDEFS
82  //----------------------------
83 
84  typedef T value_type; ///< A type that represents the data type stored in a concurrent queue.
85  typedef Alloc allocator_type; ///< A type that represents the allocator class for the concurrent queue.
86  typedef value_type& reference; ///< A type that provides a reference to an element stored in a concurrent queue.
87  typedef std::condition_variable_any condition_type; ///< A type that provides a waitable condition of the concurrent queue.
88  typedef const value_type& const_reference; ///< A type that provides a reference to a const element stored in a concurrent queue for reading and performing const operations.
89  typedef typename std::allocator_traits<allocator_type>::pointer pointer; ///< A type that provides a pointer to an element stored in a concurrent queue.
90  typedef typename std::allocator_traits<allocator_type>::const_pointer const_pointer; ///< A type that provides a const pointer to an element stored in a concurrent queue.
91  typedef Queue<T, Alloc> queue_type; ///< A type that represents the underlying non-thread-safe data structure for the concurrent queue
92  typedef typename queue_type::iterator iterator; ///< A type that represents a non-thread-safe iterator over the elements in a concurrent queue.
93  typedef typename queue_type::const_iterator const_iterator; ///< A type that represents a non-thread-safe const iterator over elements in a concurrent queue.
94  typedef typename std::reverse_iterator<iterator> reverse_iterator; ///< A type that represents a reverse non-thread-safe iterator over the elements in a concurrent queue.
95  typedef typename std::reverse_iterator<const_iterator> const_reverse_iterator; ///< A type that represents a reverse non-thread-safe const iterator over elements in a concurrent queue.
96  typedef std::shared_timed_mutex mutex_type; ///< A type that represents the mutex protecting the concurrent queue.
97  typedef typename std::shared_lock<mutex_type> read_lock_type; ///< A type representing a lock on the concurrent queue's mutex which is sufficient to read data in a thread-safe manner.
98  typedef typename std::unique_lock<mutex_type> write_lock_type; ///< A type representing a lock on the concurrent queue's mutex which is sufficient to write data in a thread-safe manner.
99  typedef typename std::iterator_traits<iterator>::difference_type difference_type; ///< A type that provides the signed distance between two elements in a concurrent queue.
100  typedef size_t size_type; ///< A type that counts the number of elements in a concurrent queue.
101 
102 public:
103  //----------------------------
104  // CONSTRUCTORS
105  //----------------------------
106 
107  /// @{
108  /// @name Constructors
109 
110  /// @brief Default Constructor.
111  /// @details Constructs an empty container, with no elements.
112  /// @param[in] alloc optional memory allocator.
113  inline concurrent_queue() = default;
114 
115  /// @brief Default Constructor.
116  /// @details Constructs an empty container, with no elements.
117  /// @param[in] alloc optional memory allocator.
118  inline explicit concurrent_queue(const allocator_type& alloc)
119  : queue(alloc)
120  {
121  }
122 
123  /// @brief Fill Constructor
124  /// @details Constructs a container with n elements. Each element is a copy of val (if provided).
125  /// @param[in] n number of elements
126  /// @param[in] alloc optional memory allocator.
127  inline explicit concurrent_queue(size_type n, const allocator_type& alloc = allocator_type())
128  : queue(n, alloc)
129  {
130  }
131 
132  /// @brief Fill Constructor
133  /// @details Constructs a container with n elements. Each element is a copy of val (if provided).
134  /// @param[in] n number of elements
135  /// @param[in] val value to fill the concurrent queue with
136  /// @param[in] alloc optional memory allocator.
137  inline concurrent_queue(size_type n, const value_type& val, const allocator_type& alloc = allocator_type())
138  : queue(n, val, alloc)
139  {
140  }
141 
142  /// @brief Range Constructor
143  /// @details Constructs a container with as many elements as the range [first,last), with each element
144  /// emplace-constructed from its corresponding element in that range, in the same order.
145  /// @tparam InputIterator Input Iterator to value_type
146  /// @param[in] first Iterator to the first element of the range
147  /// @param[in] last Iterator to the one-past-last element of the range
148  template<typename InputIterator>
149  inline concurrent_queue(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type())
150  : queue(first, last, alloc)
151  {
152  }
153 
154  /// @brief Copy Constructor
155  /// @details Constructs a container with a copy of each of the elements in x, in the same order. Thread-safe.
156  /// @param[in] other queue to copy
157  inline concurrent_queue(const concurrent_queue& other)
158  : concurrent_queue(other, read_lock_type(other.mutex))
159  {
160  }
161 
162  /// @brief Copy Constructor with allocator
163  /// @details Constructs a container with a copy of each of the elements in x, in the same order. Thread-safe.
164  /// @param[in] other queue to copy
165  /// @param[in] alloc optional memory allocator.
166  inline concurrent_queue(const concurrent_queue& other, const allocator_type& alloc)
167  : concurrent_queue(other, alloc, read_lock_type(other.mutex))
168  {
169  }
170 
171  /// @brief Move Constructor
172  /// @details Constructs a container that acquires the elements of `other`. Ownership of the contained elements is directly
173  /// transferred. `other` is left in an unspecified but valid state.
174  /// @param[in] other container to move from
175  inline concurrent_queue(concurrent_queue&& other) noexcept
176  : concurrent_queue(std::move(other), write_lock_type(other.mutex))
177  {
178  }
179 
180  /// @brief Move Constructor
181  /// @details Constructs a container that acquires the elements of `other`. Ownership of the contained elements is directly
182  /// transferred. `other` is left in an unspecified but valid state.
183  /// @param[in] other container to move from
184  inline concurrent_queue(concurrent_queue&& other, const allocator_type& alloc) noexcept
185  : concurrent_queue(std::move(other), alloc, write_lock_type(other.mutex))
186  {
187  }
188 
189  /// @brief Initializer List Constructor
190  /// @param[in] init initializer list to initialize the elements of the container with
191  /// @param[in] alloc allocator to use for all memory allocations of this container
192  inline concurrent_queue(std::initializer_list<T> init, const allocator_type& alloc = allocator_type())
193  : concurrent_queue(init.begin(), init.end(), alloc)
194  {
195  }
196 
197  /// @}
198 
199  //----------------------------
200  // DESTRUCTOR
201  //----------------------------
202 
203  /// @{
204  /// @name Destructor
205 
206  /// @brief Destructor
207  /// @details Destructs the concurrent queue. The destructors of the elements are called and the used storage is deallocated.
208  /// Note, that if the elements are pointers, the pointed-to objects are not destroyed.
209  inline ~concurrent_queue() = default;
210 
211  /// @}
212 
213  //----------------------------
214  // ASSIGNMENT OPERATORS
215  //----------------------------
216  /// @{
217  /// @name Assignment Operators
218 
219  /// @brief Copy Assignment Operator
220  /// @param[in] other concurrent queue whose elements are to be copied.
221  /// @return A reference to this concurrent queue.
223  {
224  // acquire appropriate locks on both containers
225  write_lock_type lock_this(this->mutex, std::defer_lock);
226  read_lock_type lock_that(other.mutex, std::defer_lock);
227  std::scoped_lock lock(lock_this, lock_that);
228 
229  queue = other.queue;
230  new_element.notify_all();
231  return *this;
232  }
233 
234  /// @brief Move Assignment Operator
235  /// @param[in] other concurrent queue whose elements are to be moved.
236  /// @return A reference to this concurrent queue.
237  inline concurrent_queue& operator=(concurrent_queue&& other) noexcept
238  {
239  // acquire appropriate locks on both containers
240  write_lock_type lock_this(this->mutex, std::defer_lock);
241  write_lock_type lock_that(other.mutex, std::defer_lock);
242  std::scoped_lock lock(lock_this, lock_that);
243 
244  queue = std::move(other.queue);
245  new_element.notify_all();
246  return *this;
247  }
248 
249  /// @}
250 
251  //----------------------------
252  // THREAD-SAFE PUBLIC METHODS
253  //----------------------------
254 
255  /// @{
256  /// @name Thread-safe Public Members
257  /// @details These methods are safe for use in situations where the queue will be concurrently accessed by
258  /// multiple threads.
259 
260  /// @brief Clears the concurrent queue, destroying any currently enqueued elements.
261  /// @details This method is not concurrency-safe, and invalidates all iterators.
262  inline void clear() noexcept
263  {
264  write_lock_type lock_this(this->mutex);
265  queue.clear();
266  }
267 
268  /// @brief Constructs a new element in place at the end of the concurrent queue.
269  /// @details This method is concurrency-safe. This method is concurrency-safe with respect to calls to the
270  /// methods `push`, `emplace`, `pop`, and `empty`.
271  /// @tparam Args Types of args, generally deduced automatically.
272  /// @param[in] args Arguments to forward to the constructor of the element.
273  template<class... Args>
274  inline void emplace(Args&&... args)
275  {
276  write_lock_type lock_this(this->mutex);
277  queue.emplace_back(std::forward<Args>(args)...);
278  new_element.notify_one();
279  }
280 
281  /// @brief Tests if the concurrent queue is empty at the moment this method is called.
282  /// @details This method is concurrency-safe. While this method is concurrency-safe with
283  /// respect to calls to the methods `push`, `emplace`, `pop`, and `empty`, the value returned
284  /// might be incorrect by the time it is inspected by the calling thread.
285  /// @return true if the concurrent queue was empty at the moment we looked, false otherwise.
286  [[nodiscard]] inline bool empty() const noexcept
287  {
288  read_lock_type lock_this(this->mutex);
289  return queue.empty();
290  }
291 
292  /// @brief Returns a copy of the allocator used to construct the concurrent queue.
293  /// @details This method is concurrency-safe.
294  /// @return A copy of the allocator used to construct the concurrent queue.
295  [[nodiscard]] inline allocator_type get_allocator() const noexcept
296  {
297  read_lock_type lock_this(this->mutex);
298  return queue.get_allocator();
299  }
300 
301  /// @brief Enqueues an item at tail end of the concurrent queue.
302  /// @details This method is concurrency-safe. `push` is concurrency-safe with respect to calls to the methods `push`
303  /// `emplace`, `pop`, and `empty`.
304  /// @param[in] value The item to be added to the queue.
305  inline void push(const T& value)
306  {
307  write_lock_type lock_this(this->mutex);
308  queue.push_back(value);
309  new_element.notify_one();
310  }
311 
312  /// @brief Enqueues an item at tail end of the concurrent queue.
313  /// @details This method is concurrency-safe. `push` is concurrency-safe with respect to calls to the methods `push`
314  /// `emplace`, `pop`, and `empty`.
315  /// @param[in] value The item to be added to the queue.
316  inline void push(T&& value)
317  {
318  write_lock_type lock_this(this->mutex);
319  queue.push_back(std::move(value));
320  new_element.notify_one();
321  }
322 
323  /// @brief Returns the number of items in the queue.
324  /// @details This method is concurrency-safe. `push` is concurrency-safe with respect to calls to the methods `push`
325  /// `emplace`, `try_pop`, and `empty`.
326  /// @remarks While calls to size are concurrency-safe in that they cannot damage the internal state of the concurrent
327  /// queue, it is unwise to use the results as a condition of a `for` loop or for iteration, because the
328  /// size of the container could change between the call to `size` and the invocation of the loop's methods.
329  /// @return The size of the concurrent queue.
330  size_t size() const
331  {
332  read_lock_type lock_this(this->mutex);
333  return queue.size();
334  }
335 
336  /// @brief Dequeues an item from the queue if one is available.
337  /// @details This method is concurrency-safe. If an item was successfully dequeued, the parameter `destination`
338  /// receives the dequeued value, the original value held in the queue is destroyed, and this function
339  /// returns true. If there was no item to dequeue, this function returns false without blocking, and
340  /// the contents of the `destination` parameter are undefined. A false return value does not necessarily
341  /// mean the queue is empty. `try_pop` is concurrency-safe with respect to calls to the methods `emplace`,
342  /// `push`, `try_pop`, and `empty`.
343  /// @param[out] destination A reference to a location to store the dequeued item.
344  /// @return true if an item was successfully dequeued, false otherwise.
345  inline bool try_pop(T& destination)
346  {
347  // In a perfect world, we would get a read lock, check whether the queue was empty, then upgrade that
348  // to a write lock. However, there's no way to do that in C++17 (or 20). Should it ever become possible,
349  // this block should be updated. For now, we'll just try to get write permissions from the onset and return
350  // if we fail to get ownership of the lock to keep `pop` as snappy as possible.
351  write_lock_type lock_this(this->mutex, std::defer_lock);
352 
353  // if it can't acquire the lock, or it did get the lock but the queue is empty, return false;
354  if (!lock_this.try_lock() || queue.empty())
355  return false;
356 
357  // we have the lock at this point. Move and pop the front of the queue
358  destination = std::move(queue.front());
359  queue.pop_front();
360  return true;
361  }
362 
363  /// @brief Dequeues an item from the queue if one is available within the specified timeout.
364  /// @details This method is concurrency-safe. If an item was successfully dequeued, the parameter `destination`
365  /// receives the dequeued value, the original value held in the queue is destroyed, and this function
366  /// returns true. If there was no item to dequeue, this function returns false without blocking, and
367  /// the contents of the `destination` parameter are undefined. A false return value does not necessarily
368  /// mean the queue is empty. `try_pop` is concurrency-safe with respect to calls to the methods `emplace`,
369  /// `push`, `try_pop`, and `empty`.
370  /// @param[out] destination A reference to a location to store the dequeued item.
371  /// @param[in] timeout_duration The maximum length of time `try_pop_for` will attempt to pop the queue before declaring
372  /// failure.
373  /// @return true if an item was successfully dequeued, false otherwise.
374  template<class Rep, class Period>
375  inline bool try_pop_for(T& destination, const std::chrono::duration<Rep, Period>& timeout_duration)
376  {
377  auto start = std::chrono::steady_clock::now();
378  write_lock_type lock_this(this->mutex, std::defer_lock);
379 
380  // return if we can't get the lock within the timeout period
381  if (!lock_this.try_lock_for(timeout_duration))
382  return false;
383 
384  // We have the lock now. If we haven't timed out, wait for the queue to have contents
385  while (queue.empty())
386  {
387  auto elapsed = std::chrono::steady_clock::now() - start;
388  if (elapsed > timeout_duration || new_element.wait_for(lock_this, timeout_duration - elapsed) == std::cv_status::timeout)
389  return false;
390  }
391 
392  // At this point the queue is not empty
393  destination = std::move(queue.front());
394  queue.pop_front();
395  return true;
396  }
397 
398  /// @}
399 
400  //----------------------------
401  // THREAD-UNSAFE ACCESSORS
402  //----------------------------
403  /// @{
404  /// @name Thread-unsafe Public Members
405  /// @details These methods are not recommended for use in production code and will generate warnings when used. That said, they're
406  /// helpful for testing and debug, which is why they are included.
407 
408  /// @brief Returns an iterator of type iterator to the beginning of the concurrent queue.
409  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
410  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
411  /// with respect to other queue operations.
412  /// @return An iterator of type iterator to the beginning of the concurrent queue.
413 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
414  [[deprecated("concurrent_queue::begin() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
415 #endif
416  inline iterator
418  {
419  return queue.begin();
420  }
421 
422  /// @brief Returns an iterator of type const_iterator to the beginning of the concurrent queue.
423  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
424  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
425  /// with respect to other queue operations.
426  /// @return An iterator of type const_iterator to the beginning of the concurrent queue.
427 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
428  [[deprecated("concurrent_queue::begin() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
429 #endif
430  inline const_iterator
431  begin() const
432  {
433  return queue.cbegin();
434  }
435 
436  /// @brief Returns an iterator of type const_iterator to the beginning of the concurrent queue.
437  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
438  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
439  /// with respect to other queue operations.
440  /// @return An iterator of type const_iterator to the beginning of the concurrent queue.
441 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
442  [[deprecated("concurrent_queue::cbegin() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
443 #endif
444  inline const_iterator
445  cbegin() const
446  {
447  return queue.cbegin();
448  }
449 
450  /// @brief Returns an iterator of type iterator to the end of the concurrent queue.
451  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
452  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
453  /// with respect to other queue operations.
454  /// @return An iterator of type iterator to the end of the concurrent queue.
455 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
456  [[deprecated("concurrent_queue::end() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
457 #endif
458  inline iterator
459  end()
460  {
461  return queue.end();
462  }
463 
464  /// @brief Returns an iterator of type const_iterator to the end of the concurrent queue.
465  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
466  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
467  /// with respect to other queue operations.
468  /// @return An iterator of type const_iterator to the end of the concurrent queue.
469 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
470  [[deprecated("concurrent_queue::end() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
471 #endif
472  inline const_iterator
473  end() const
474  {
475  return queue.cend();
476  }
477 
478  /// @brief Returns an iterator of type const_iterator to the end of the concurrent queue.
479  /// @details This method is not concurrency-safe. The iterators for the concurrent_queue class are
480  /// primarily intended for debugging, as they are slow, and iteration is not concurrency-safe
481  /// with respect to other queue operations.
482  /// @return An iterator of type const_iterator to the end of the concurrent queue.
483 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
484  [[deprecated("concurrent_queue::cend() is not thread-safe. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
485 #endif
486  inline const_iterator
487  cend() const
488  {
489  return queue.cend();
490  }
491 
492  /// @}
493 
494  //----------------------------
495  // [TEST USE] ACCESS CONTROL
496  //----------------------------
497  // These functions are primarily intended for testing/debugging, and are NOT the recommended interface for using
498  // the concurrent queue (prefer `push` and `try_pop`). However, they may have some limited utility when iteration
499  // is absolutely required.
500 
501  /// @{
502  /// @name Access Control
503  /// @details These methods provide the ability to lock the concurrent_queue for thread-safe iteration. They are
504  /// primarily intended for test and debug use, and care must be taken when explicitely locking the queue to
505  /// avoid deadlock. These methods are NOT recommended for production code, and will produce warnings when used.
506 
507  /// @brief Lock the queue in a manner in which it is safe for multiple threads to read-iterate over it
508  /// @details Allows concurrency-safe iteration.
509  /// @remarks Intended for test and debug use only.
510  /// @return RAII lock suitable for safe read-iteration. Note that failure to move from or bind to the return value
511  /// will invoke the locks destructor at the end of the call, instantly unlocking the concurrent queue.
512 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
513  [[deprecated("concurrent_queue::acquire_read_lock() is not recommended for production code. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
514 #endif
515  [[nodiscard]] inline read_lock_type
517  {
518  return read_lock_type(this->mutex);
519  }
520 
521  /// @brief Lock the queue in a manner in which it is safe for multiple threads to write-iterate over it
522  /// @details Allows concurrency-safe iteration.
523  /// @remarks Intended for test and debug use only.
524  /// @return RAII lock suitable for safe write-iteration. Note that failure to move from or bind to the return value
525  /// will invoke the locks destructor at the end of the call, instantly unlocking the concurrent queue.
526 #ifndef _CONCURRENT_QUEUE_NO_WARNINGS
527  [[deprecated("concurrent_queue::acquire_write_lock() is not recommended for production code. Define `_CONCURRENT_QUEUE_NO_WARNINGS` to disable this message")]]
528 #endif
529  [[nodiscard]] inline write_lock_type
531  {
532  return write_lock_type(this->mutex);
533  }
534 
535  /// @}
536 
537  //----------------------------
538  // EQUALITY OPERATORS
539  //----------------------------
540 
541  /// @{
542  /// @name Equality Operators
543 
544  /// @brief Equality Operator
545  /// @param rhs Right-hand side of the comparison
546  /// @return true if the contents of the queues are equivalent, false otherwise.
547  template<class Ty, template<class, class> class Q, class A>
548  friend bool operator==(const concurrent_queue<Ty, Q, A>& lhs, const concurrent_queue<Ty, Q, A>& rhs);
549 
550  /// @brief Inequality Operator
551  /// @param rhs Right-hand side of the comparison
552  /// @return false if the contents of the queues are equivalent, true otherwise.
553  template<class Ty, template<class, class> class Q, class A>
554  friend bool operator!=(const concurrent_queue<Ty, Q, A>& lhs, const concurrent_queue<Ty, Q, A>& rhs);
555 
556  /// @}
557 
558  //----------------------------
559  // SWAP
560  //----------------------------
561 
562  /// @{
563  /// @name Swap Operator
564 
565  /// @brief Swap Operator
566  /// @param lhs container to swap with rhs
567  /// @param rhs container to swap with lhs
568  template<class Ty, template<class, class> class Q, class A>
569  friend void std::swap(concurrent_queue<Ty, Q, A>& lhs, concurrent_queue<Ty, Q, A>& rhs);
570 
571  /// @}
572 
573 private:
574  //----------------------------
575  // PRIVATE CONSTRUCTOR IMPLS
576  //----------------------------
577 
578  /// @brief Copy Constructor Implementation
579  /// @details by taking the lock as a parameter, it allows the queue to be copy-constructed, rather than copy-assigned
580  /// @param[in] other container to copy
581  /// @param[in] read_lock lock that has been locked on other.mutex
582  inline concurrent_queue(const concurrent_queue& other, read_lock_type)
583  : queue(other.queue)
584  {
585  }
586 
587  /// @brief Copy Constructor Implementation
588  /// @details by taking the lock as a parameter, it allows the queue to be copy-constructed, rather than copy-assigned
589  /// @param[in] other container to copy
590  /// @param[in] alloc memory allocator
591  /// @param[in] read_lock lock that has been locked on other.mutex
592  inline concurrent_queue(const concurrent_queue& other, const allocator_type& alloc, read_lock_type)
593  : queue(other.queue, alloc)
594  {
595  }
596 
597  /// @brief Move Constructor Implementation
598  /// @details by taking the lock as a parameter, it allows the queue to be move-constructed, rather than move-assigned
599  /// @param[in] other container to move
600  /// @param[in] read_lock lock that has been locked on other.mutex
601  inline concurrent_queue(concurrent_queue&& other, write_lock_type) noexcept
602  : queue(std::move(other.queue))
603  {
604  }
605 
606  /// @brief move Constructor Implementation
607  /// @details by taking the lock as a parameter, it allows the queue to be move-constructed, rather than move-assigned
608  /// @param[in] other container to move
609  /// @param[in] alloc memory allocator
610  /// @param[in] read_lock lock that has been locked on other.mutex
611  inline concurrent_queue(concurrent_queue&& other, const allocator_type& alloc, write_lock_type) noexcept
612  : queue(std::move(other.queue), alloc)
613  {
614  }
615 
616 private:
617  //----------------------------
618  // PRIVATE MEMBERS
619  //----------------------------
620  // It's probably best to initialize
621  // the mutex before the queue
622 
623  mutable mutex_type mutex;
624  condition_type new_element;
625  queue_type queue;
626 };
627 
628 /// @brief Equality Operator
629 /// @return true if the queues are element-by-element equivalent
630 template<class T, template<class, class> class Queue = std::deque, class Alloc = std::allocator<T>>
631 inline bool operator==(const concurrent_queue<T, Queue, Alloc>& lhs, const concurrent_queue<T, Queue, Alloc>& rhs)
632 {
633  typename concurrent_queue<T, Queue, Alloc>::read_lock_type lock_lhs(lhs.mutex, std::defer_lock);
634  typename concurrent_queue<T, Queue, Alloc>::read_lock_type lock_rhs(rhs.mutex, std::defer_lock);
635  std::scoped_lock lock(lock_lhs, lock_rhs);
636 
637  if (lhs.queue.size() != rhs.queue.size())
638  return false;
639 
640  for (int i = 0; i < lhs.queue.size(); ++i)
641  if (lhs.queue[i] != rhs.queue[i])
642  return false;
643 
644  return true;
645 }
646 
647 /// @brief Inequality Operator
648 /// @return true if the queues are not element-by-element equivalent
649 template<class T, template<class, class> class Queue = std::deque, class Alloc = std::allocator<T>>
650 inline bool operator!=(const concurrent_queue<T, Queue, Alloc>& lhs, const concurrent_queue<T, Queue, Alloc>& rhs)
651 {
652  return !(lhs == rhs);
653 }
654 
655 namespace std
656 {
657  /// @brief Swap Operator
658  template<class T, template<class, class> class Queue = std::deque, class Alloc = std::allocator<T>>
659  inline void swap(concurrent_queue<T, Queue, Alloc>& lhs, concurrent_queue<T, Queue, Alloc>& rhs)
660  {
661  if (&lhs != &rhs)
662  {
663  typename concurrent_queue<T, Queue, Alloc>::read_lock_type lock_lhs(lhs.mutex, std::defer_lock);
664  typename concurrent_queue<T, Queue, Alloc>::read_lock_type lock_rhs(rhs.mutex, std::defer_lock);
665  std::scoped_lock lock(lock_lhs, lock_rhs);
666 
667  std::swap(lhs.queue, rhs.queue);
668  }
669  }
670 } // namespace std
671 
672 #endif // concurrent_queue_h__
concurrent_queue::concurrent_queue
concurrent_queue(const concurrent_queue &other, const allocator_type &alloc)
Copy Constructor with allocator.
Definition: concurrent_queue.h:166
concurrent_queue::size
size_t size() const
Returns the number of items in the queue.
Definition: concurrent_queue.h:330
concurrent_queue::get_allocator
allocator_type get_allocator() const noexcept
Returns a copy of the allocator used to construct the concurrent queue.
Definition: concurrent_queue.h:295
concurrent_queue::end
iterator end()
Returns an iterator of type iterator to the end of the concurrent queue.
Definition: concurrent_queue.h:459
concurrent_queue::acquire_read_lock
read_lock_type acquire_read_lock() const
Lock the queue in a manner in which it is safe for multiple threads to read-iterate over it.
Definition: concurrent_queue.h:516
concurrent_queue::concurrent_queue
concurrent_queue(const concurrent_queue &other)
Copy Constructor.
Definition: concurrent_queue.h:157
operator!=
bool operator!=(const concurrent_queue< T, Queue, Alloc > &lhs, const concurrent_queue< T, Queue, Alloc > &rhs)
Inequality Operator.
Definition: concurrent_queue.h:650
concurrent_queue::operator=
concurrent_queue & operator=(const concurrent_queue &other)
Copy Assignment Operator.
Definition: concurrent_queue.h:222
concurrent_queue::const_iterator
queue_type::const_iterator const_iterator
A type that represents a non-thread-safe const iterator over elements in a concurrent queue.
Definition: concurrent_queue.h:93
concurrent_queue::push
void push(T &&value)
Enqueues an item at tail end of the concurrent queue.
Definition: concurrent_queue.h:316
concurrent_queue::size_type
size_t size_type
A type that counts the number of elements in a concurrent queue.
Definition: concurrent_queue.h:100
concurrent_queue::end
const_iterator end() const
Returns an iterator of type const_iterator to the end of the concurrent queue.
Definition: concurrent_queue.h:473
concurrent_queue::queue_type
Queue< T, Alloc > queue_type
A type that represents the underlying non-thread-safe data structure for the concurrent queue.
Definition: concurrent_queue.h:91
concurrent_queue::concurrent_queue
concurrent_queue(const allocator_type &alloc)
Default Constructor.
Definition: concurrent_queue.h:118
concurrent_queue::operator==
friend bool operator==(const concurrent_queue< Ty, Q, A > &lhs, const concurrent_queue< Ty, Q, A > &rhs)
Equality Operator.
concurrent_queue::push
void push(const T &value)
Enqueues an item at tail end of the concurrent queue.
Definition: concurrent_queue.h:305
concurrent_queue::try_pop_for
bool try_pop_for(T &destination, const std::chrono::duration< Rep, Period > &timeout_duration)
Dequeues an item from the queue if one is available within the specified timeout.
Definition: concurrent_queue.h:375
concurrent_queue::~concurrent_queue
~concurrent_queue()=default
Destructor.
concurrent_queue::empty
bool empty() const noexcept
Tests if the concurrent queue is empty at the moment this method is called.
Definition: concurrent_queue.h:286
concurrent_queue::begin
const_iterator begin() const
Returns an iterator of type const_iterator to the beginning of the concurrent queue.
Definition: concurrent_queue.h:431
concurrent_queue::read_lock_type
std::shared_lock< mutex_type > read_lock_type
A type representing a lock on the concurrent queue's mutex which is sufficient to read data in a thre...
Definition: concurrent_queue.h:97
concurrent_queue::concurrent_queue
concurrent_queue(size_type n, const value_type &val, const allocator_type &alloc=allocator_type())
Fill Constructor.
Definition: concurrent_queue.h:137
concurrent_queue::emplace
void emplace(Args &&... args)
Constructs a new element in place at the end of the concurrent queue.
Definition: concurrent_queue.h:274
concurrent_queue::const_reverse_iterator
std::reverse_iterator< const_iterator > const_reverse_iterator
A type that represents a reverse non-thread-safe const iterator over elements in a concurrent queue.
Definition: concurrent_queue.h:95
std::swap
void swap(concurrent_queue< T, Queue, Alloc > &lhs, concurrent_queue< T, Queue, Alloc > &rhs)
Swap Operator.
Definition: concurrent_queue.h:659
concurrent_queue::iterator
queue_type::iterator iterator
A type that represents a non-thread-safe iterator over the elements in a concurrent queue.
Definition: concurrent_queue.h:92
concurrent_queue::cbegin
const_iterator cbegin() const
Returns an iterator of type const_iterator to the beginning of the concurrent queue.
Definition: concurrent_queue.h:445
concurrent_queue::value_type
T value_type
A type that represents the data type stored in a concurrent queue.
Definition: concurrent_queue.h:84
operator==
bool operator==(const concurrent_queue< T, Queue, Alloc > &lhs, const concurrent_queue< T, Queue, Alloc > &rhs)
Equality Operator.
Definition: concurrent_queue.h:631
concurrent_queue::concurrent_queue
concurrent_queue(std::initializer_list< T > init, const allocator_type &alloc=allocator_type())
Initializer List Constructor.
Definition: concurrent_queue.h:192
concurrent_queue::acquire_write_lock
write_lock_type acquire_write_lock()
Lock the queue in a manner in which it is safe for multiple threads to write-iterate over it.
Definition: concurrent_queue.h:530
concurrent_queue::begin
iterator begin()
Returns an iterator of type iterator to the beginning of the concurrent queue.
Definition: concurrent_queue.h:417
concurrent_queue::const_reference
const typedef value_type & const_reference
A type that provides a reference to a const element stored in a concurrent queue for reading and perf...
Definition: concurrent_queue.h:88
concurrent_queue
The concurrent_queue class is a sequence container class that allows first-in, first-out access to it...
Definition: concurrent_queue.h:77
concurrent_queue::const_pointer
std::allocator_traits< allocator_type >::const_pointer const_pointer
A type that provides a const pointer to an element stored in a concurrent queue.
Definition: concurrent_queue.h:90
concurrent_queue::allocator_type
Alloc allocator_type
A type that represents the allocator class for the concurrent queue.
Definition: concurrent_queue.h:85
concurrent_queue::operator=
concurrent_queue & operator=(concurrent_queue &&other) noexcept
Move Assignment Operator.
Definition: concurrent_queue.h:237
concurrent_queue::pointer
std::allocator_traits< allocator_type >::pointer pointer
A type that provides a pointer to an element stored in a concurrent queue.
Definition: concurrent_queue.h:89
concurrent_queue::try_pop
bool try_pop(T &destination)
Dequeues an item from the queue if one is available.
Definition: concurrent_queue.h:345
concurrent_queue::concurrent_queue
concurrent_queue(size_type n, const allocator_type &alloc=allocator_type())
Fill Constructor.
Definition: concurrent_queue.h:127
concurrent_queue::swap
friend void std::swap(concurrent_queue< Ty, Q, A > &lhs, concurrent_queue< Ty, Q, A > &rhs)
Swap Operator.
concurrent_queue::mutex_type
std::shared_timed_mutex mutex_type
A type that represents the mutex protecting the concurrent queue.
Definition: concurrent_queue.h:96
concurrent_queue::condition_type
std::condition_variable_any condition_type
A type that provides a waitable condition of the concurrent queue.
Definition: concurrent_queue.h:87
concurrent_queue::cend
const_iterator cend() const
Returns an iterator of type const_iterator to the end of the concurrent queue.
Definition: concurrent_queue.h:487
concurrent_queue::operator!=
friend bool operator!=(const concurrent_queue< Ty, Q, A > &lhs, const concurrent_queue< Ty, Q, A > &rhs)
Inequality Operator.
concurrent_queue::write_lock_type
std::unique_lock< mutex_type > write_lock_type
A type representing a lock on the concurrent queue's mutex which is sufficient to write data in a thr...
Definition: concurrent_queue.h:98
concurrent_queue::difference_type
std::iterator_traits< iterator >::difference_type difference_type
A type that provides the signed distance between two elements in a concurrent queue.
Definition: concurrent_queue.h:99
concurrent_queue::concurrent_queue
concurrent_queue(concurrent_queue &&other, const allocator_type &alloc) noexcept
Move Constructor.
Definition: concurrent_queue.h:184
concurrent_queue::concurrent_queue
concurrent_queue()=default
Default Constructor.
concurrent_queue::reference
value_type & reference
A type that provides a reference to an element stored in a concurrent queue.
Definition: concurrent_queue.h:86
concurrent_queue::concurrent_queue
concurrent_queue(InputIterator first, InputIterator last, const allocator_type &alloc=allocator_type())
Range Constructor.
Definition: concurrent_queue.h:149
concurrent_queue::concurrent_queue
concurrent_queue(concurrent_queue &&other) noexcept
Move Constructor.
Definition: concurrent_queue.h:175
concurrent_queue::clear
void clear() noexcept
Clears the concurrent queue, destroying any currently enqueued elements.
Definition: concurrent_queue.h:262