147147#ifndef WAIT_QUEUE_HPP_INCLUDED
148148#define WAIT_QUEUE_HPP_INCLUDED
149149
150+ #include < cassert> // assert
150151#include < deque>
151152#include < mutex> // std::scoped_lock, std::mutex
152153#include < condition_variable>
@@ -185,23 +186,35 @@ class wait_queue {
185186 * anything, so a different @c wait_queue constructor must be used if
186187 * instantiated with a @c boost @c circular_buffer.
187188 *
189+ * @post @c empty returns true.
190+ * @post @c size returns 0.
191+ * @post @c stop_requested return false.
188192 */
189193 wait_queue ()
190194 // noexcept(std::is_nothrow_constructible<Container>::value)
191- : m_mut(), m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
192- m_data_cond (), m_data_queue() { }
195+ : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token())
196+ {
197+ assert (empty ());
198+ assert (size () == size_type (0 ));
199+ assert (!stop_requested ());
200+ }
193201
194202 /* *
195203 * @brief Construct a @c wait_queue with an externally provided @c std::stop_token.
196204 *
197205 * @param stop_tok A @c std::stop_token which can be used to shutdown @c wait_queue
198206 * processing.
199207 *
208+ * @post @c empty returns true.
209+ * @post @c size returns 0.
200210 */
201211 wait_queue (std::stop_token stop_tok)
202212 // noexcept(std::is_nothrow_constructible<Container>::value)
203- : m_mut(), m_stop_src(), m_stop_tok(stop_tok),
204- m_data_cond (), m_data_queue() { }
213+ : m_stop_tok(stop_tok)
214+ {
215+ assert (empty ());
216+ assert (size () == size_type (0 ));
217+ }
205218
206219 /* *
207220 * @brief Construct a @c wait_queue with an iterator range for the container.
@@ -221,12 +234,19 @@ class wait_queue {
221234 * @param beg Beginning iterator.
222235 *
223236 * @param end Ending iterator.
237+ *
238+ * @post @c empty returns true if @c beg equals @c end otherwise returns false.
239+ * @post @c size returns the distance between @c beg and @c end parameters.
224240 */
225241 template <typename Iter>
226242 wait_queue (Iter beg, Iter end)
227243 // noexcept(std::is_nothrow_constructible<Container, Iter, Iter>::value)
228- : m_mut(), m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
229- m_data_cond(), m_data_queue(beg, end) { }
244+ : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
245+ m_data_queue (beg, end)
246+ {
247+ assert (empty () == (beg == end));
248+ assert ((size () == size_type (0 )) == (beg == end)); // std::distance constrains beg, end.
249+ }
230250
231251 /* *
232252 * @brief Construct a @c wait_queue with an iterator range and a @c std::stop_token.
@@ -237,12 +257,18 @@ class wait_queue {
237257 * @param beg Beginning iterator.
238258 *
239259 * @param end Ending iterator.
260+ *
261+ * @post @c empty returns true if @c beg equals @c end otherwise returns false.
262+ * @post @c size returns the distance between @c beg and @c end parameters.
240263 */
241264 template <typename Iter>
242265 wait_queue (std::stop_token stop_tok, Iter beg, Iter end)
243266 // noexcept(std::is_nothrow_constructible<Container, Iter, Iter>::value)
244- : m_mut(), m_stop_src(), m_stop_tok(stop_tok),
245- m_data_cond(), m_data_queue(beg, end) { }
267+ : m_stop_tok(stop_tok), m_data_queue(beg, end)
268+ {
269+ assert (empty () == (beg == end));
270+ assert ((size () == size_type (0 )) == (beg == end)); // std::distance constrains beg, end.
271+ }
246272
247273 /* *
248274 * @brief Construct a @c wait_queue with an initial size or capacity.
@@ -263,11 +289,17 @@ class wait_queue {
263289 *
264290 * @param sz Capacity or initial size, depending on container type.
265291 *
292+ * @post If @c sz is 0 @c empty returns true, else behavior depends on container used.
293+ * @post @c size returns 0 or @c sz depending on container used.
266294 */
267295 wait_queue (size_type sz)
268296 // noexcept(std::is_nothrow_constructible<Container, size_type>::value)
269- : m_mut(), m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
270- m_data_cond(), m_data_queue(sz) { }
297+ : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
298+ m_data_queue (sz)
299+ {
300+ assert ((sz != size_type (0 )) || empty ());
301+ assert ((size () == size_type (0 )) || (size () == sz));
302+ }
271303
272304 /* *
273305 * @brief Construct a @c wait_queue with an initial size or capacity along
@@ -278,11 +310,16 @@ class wait_queue {
278310 *
279311 * @param sz Capacity or initial size, depending on container type.
280312 *
313+ * @post If @c sz is 0 @c empty returns true, else behavior depends on container used.
314+ * @post @c size returns 0 or @c sz depending on container used.
281315 */
282316 wait_queue (std::stop_token stop_tok, size_type sz)
283317 // noexcept(std::is_nothrow_constructible<Container, size_type>::value)
284- : m_mut(), m_stop_src(), m_stop_tok((*m_stop_src).get_token()),
285- m_data_cond(), m_data_queue(sz) { }
318+ : m_stop_tok((*m_stop_src).get_token()), m_data_queue(sz)
319+ {
320+ assert ((sz != size_type (0 )) || empty ());
321+ assert ((size () == size_type (0 )) || (size () == sz));
322+ }
286323
287324 // disallow copy or move construction of the entire object
288325 wait_queue (const wait_queue&) = delete ;
@@ -307,7 +344,6 @@ class wait_queue {
307344 * @return @c true if an internal @c stop_source was used (versus a @c std::stop_token
308345 * passed in to the constructor) and the request returns @c true, @c false if an
309346 * external @c std::stop_token was passed in.
310- *
311347 */
312348 auto request_stop () noexcept
313349 -> bool {
@@ -329,6 +365,9 @@ class wait_queue {
329365 *
330366 * @return @c true if successful, @c false if the @c wait_queue has been
331367 * requested to stop.
368+ *
369+ * @post If @c true is returned and @c empty is false, one of any threads waiting for a
370+ * value will be unblocked.
332371 */
333372 auto push (const T& val) /* noexcept(std::is_nothrow_copy_constructible<T>::value) */
334373 -> bool {
@@ -348,6 +387,9 @@ class wait_queue {
348387 *
349388 * This method has the same semantics as the other @c push, except that the value will
350389 * be moved (if possible) instead of copied.
390+ *
391+ * @post If @c true is returned and @c empty is false, one of any threads waiting for a
392+ * value will be unblocked.
351393 */
352394 auto push (T&& val) /* noexcept(std::is_nothrow_move_constructible<T>::value) */
353395 -> bool {
@@ -374,6 +416,9 @@ class wait_queue {
374416 *
375417 * @return @c true if successful, @c false if the @c wait_queue is has been requested
376418 * to stop.
419+ *
420+ * @post If @c true is returned and @c empty is false, one of any threads waiting for a
421+ * value will be unblocked.
377422 */
378423 template <typename ... Args>
379424 auto emplace_push (Args &&... args) /* noexcept(std::is_nothrow_constructible<T, Args...>::value)*/
@@ -399,6 +444,9 @@ class wait_queue {
399444 *
400445 * @return A value from the @c wait_queue (if non-empty). If the @c std::optional is empty,
401446 * the @c wait_queue has been requested to be stopped.
447+ *
448+ * @post If a non empty value is returned, until a push function is called, @c size is one
449+ * less than before this function was called.
402450 */
403451 auto wait_and_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
404452 -> std::optional<T> {
@@ -407,8 +455,13 @@ class wait_queue {
407455 if (!m_data_cond.wait ( lk, m_stop_tok, [this ] { return !m_data_queue.empty (); } )) {
408456 return std::optional<T> {}; // queue was request to stop, no data available
409457 }
458+ assert (!m_data_queue.empty ());
459+ #ifndef NDEBUG
460+ const auto old_size = m_data_queue.size ();
461+ #endif
410462 std::optional<T> val {std::move_if_noexcept (m_data_queue.front ())}; // move construct if possible
411463 m_data_queue.pop_front ();
464+ assert (m_data_queue.size () + 1u == old_size);
412465 return val;
413466
414467 }
@@ -420,6 +473,9 @@ class wait_queue {
420473 * @return A value from the @c wait_queue or an empty @c std::optional if no values are
421474 * available in the @c wait_queue or if the @c wait_queue has been requested to be
422475 * stopped .
476+ *
477+ * @post If a non empty value is returned, until a push function is called, @c size is one
478+ * less than before this function was called.
423479 */
424480 auto try_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
425481 -> std::optional<T> {
@@ -431,8 +487,12 @@ class wait_queue {
431487 if (m_data_queue.empty ()) {
432488 return std::optional<T> {};
433489 }
490+ #ifndef NDEBUG
491+ const auto old_size = m_data_queue.size ();
492+ #endif
434493 std::optional<T> val {std::move_if_noexcept (m_data_queue.front ())}; // move construct if possible
435494 m_data_queue.pop_front ();
495+ assert (m_data_queue.size () + 1u == old_size);
436496 return val;
437497
438498 }
0 commit comments