147
147
#ifndef WAIT_QUEUE_HPP_INCLUDED
148
148
#define WAIT_QUEUE_HPP_INCLUDED
149
149
150
+ #include < cassert> // assert
150
151
#include < deque>
151
152
#include < mutex> // std::scoped_lock, std::mutex
152
153
#include < condition_variable>
@@ -185,23 +186,35 @@ class wait_queue {
185
186
* anything, so a different @c wait_queue constructor must be used if
186
187
* instantiated with a @c boost @c circular_buffer.
187
188
*
189
+ * @post @c empty returns true.
190
+ * @post @c size returns 0.
191
+ * @post @c stop_requested return false.
188
192
*/
189
193
wait_queue ()
190
194
// 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
+ }
193
201
194
202
/* *
195
203
* @brief Construct a @c wait_queue with an externally provided @c std::stop_token.
196
204
*
197
205
* @param stop_tok A @c std::stop_token which can be used to shutdown @c wait_queue
198
206
* processing.
199
207
*
208
+ * @post @c empty returns true.
209
+ * @post @c size returns 0.
200
210
*/
201
211
wait_queue (std::stop_token stop_tok)
202
212
// 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
+ }
205
218
206
219
/* *
207
220
* @brief Construct a @c wait_queue with an iterator range for the container.
@@ -221,12 +234,19 @@ class wait_queue {
221
234
* @param beg Beginning iterator.
222
235
*
223
236
* @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.
224
240
*/
225
241
template <typename Iter>
226
242
wait_queue (Iter beg, Iter end)
227
243
// 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
+ }
230
250
231
251
/* *
232
252
* @brief Construct a @c wait_queue with an iterator range and a @c std::stop_token.
@@ -237,12 +257,18 @@ class wait_queue {
237
257
* @param beg Beginning iterator.
238
258
*
239
259
* @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.
240
263
*/
241
264
template <typename Iter>
242
265
wait_queue (std::stop_token stop_tok, Iter beg, Iter end)
243
266
// 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
+ }
246
272
247
273
/* *
248
274
* @brief Construct a @c wait_queue with an initial size or capacity.
@@ -263,11 +289,17 @@ class wait_queue {
263
289
*
264
290
* @param sz Capacity or initial size, depending on container type.
265
291
*
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.
266
294
*/
267
295
wait_queue (size_type sz)
268
296
// 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
+ }
271
303
272
304
/* *
273
305
* @brief Construct a @c wait_queue with an initial size or capacity along
@@ -278,11 +310,16 @@ class wait_queue {
278
310
*
279
311
* @param sz Capacity or initial size, depending on container type.
280
312
*
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.
281
315
*/
282
316
wait_queue (std::stop_token stop_tok, size_type sz)
283
317
// 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
+ }
286
323
287
324
// disallow copy or move construction of the entire object
288
325
wait_queue (const wait_queue&) = delete ;
@@ -307,7 +344,6 @@ class wait_queue {
307
344
* @return @c true if an internal @c stop_source was used (versus a @c std::stop_token
308
345
* passed in to the constructor) and the request returns @c true, @c false if an
309
346
* external @c std::stop_token was passed in.
310
- *
311
347
*/
312
348
auto request_stop () noexcept
313
349
-> bool {
@@ -329,6 +365,9 @@ class wait_queue {
329
365
*
330
366
* @return @c true if successful, @c false if the @c wait_queue has been
331
367
* 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.
332
371
*/
333
372
auto push (const T& val) /* noexcept(std::is_nothrow_copy_constructible<T>::value) */
334
373
-> bool {
@@ -348,6 +387,9 @@ class wait_queue {
348
387
*
349
388
* This method has the same semantics as the other @c push, except that the value will
350
389
* 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.
351
393
*/
352
394
auto push (T&& val) /* noexcept(std::is_nothrow_move_constructible<T>::value) */
353
395
-> bool {
@@ -374,6 +416,9 @@ class wait_queue {
374
416
*
375
417
* @return @c true if successful, @c false if the @c wait_queue is has been requested
376
418
* 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.
377
422
*/
378
423
template <typename ... Args>
379
424
auto emplace_push (Args &&... args) /* noexcept(std::is_nothrow_constructible<T, Args...>::value)*/
@@ -399,6 +444,9 @@ class wait_queue {
399
444
*
400
445
* @return A value from the @c wait_queue (if non-empty). If the @c std::optional is empty,
401
446
* 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.
402
450
*/
403
451
auto wait_and_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
404
452
-> std::optional<T> {
@@ -407,8 +455,13 @@ class wait_queue {
407
455
if (!m_data_cond.wait ( lk, m_stop_tok, [this ] { return !m_data_queue.empty (); } )) {
408
456
return std::optional<T> {}; // queue was request to stop, no data available
409
457
}
458
+ assert (!m_data_queue.empty ());
459
+ #ifndef NDEBUG
460
+ const auto old_size = m_data_queue.size ();
461
+ #endif
410
462
std::optional<T> val {std::move_if_noexcept (m_data_queue.front ())}; // move construct if possible
411
463
m_data_queue.pop_front ();
464
+ assert (m_data_queue.size () + 1u == old_size);
412
465
return val;
413
466
414
467
}
@@ -420,6 +473,9 @@ class wait_queue {
420
473
* @return A value from the @c wait_queue or an empty @c std::optional if no values are
421
474
* available in the @c wait_queue or if the @c wait_queue has been requested to be
422
475
* 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.
423
479
*/
424
480
auto try_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
425
481
-> std::optional<T> {
@@ -431,8 +487,12 @@ class wait_queue {
431
487
if (m_data_queue.empty ()) {
432
488
return std::optional<T> {};
433
489
}
490
+ #ifndef NDEBUG
491
+ const auto old_size = m_data_queue.size ();
492
+ #endif
434
493
std::optional<T> val {std::move_if_noexcept (m_data_queue.front ())}; // move construct if possible
435
494
m_data_queue.pop_front ();
495
+ assert (m_data_queue.size () + 1u == old_size);
436
496
return val;
437
497
438
498
}
0 commit comments