BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_singleproducersingleconsumerboundedqueue.h
Go to the documentation of this file.
1/// @file bdlcc_singleproducersingleconsumerboundedqueue.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_singleproducersingleconsumerboundedqueue.h -*-C++-*-
8
9#ifndef INCLUDED_BDLCC_SINGLEPRODUCERSINGLECONSUMERBOUNDEDQUEUE
10#define INCLUDED_BDLCC_SINGLEPRODUCERSINGLECONSUMERBOUNDEDQUEUE
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bdlcc_singleproducersingleconsumerboundedqueue bdlcc_singleproducersingleconsumerboundedqueue
16/// @brief Provide a thread-aware SPSC bounded queue of values.
17/// @addtogroup bdl
18/// @{
19/// @addtogroup bdlcc
20/// @{
21/// @addtogroup bdlcc_singleproducersingleconsumerboundedqueue
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-purpose"> Purpose</a>
26/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-classes"> Classes </a>
27/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-description"> Description </a>
28/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-template-requirements"> Template Requirements </a>
29/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-exception-safety"> Exception Safety </a>
30/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-move-semantics-in-c-03"> Move Semantics in C++03 </a>
31/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-usage"> Usage </a>
32/// * <a href="#bdlcc_singleproducersingleconsumerboundedqueue-example-1-a-simple-thread-pool"> Example 1: A Simple Thread Pool </a>
33///
34/// # Purpose {#bdlcc_singleproducersingleconsumerboundedqueue-purpose}
35/// Provide a thread-aware SPSC bounded queue of values.
36///
37/// # Classes {#bdlcc_singleproducersingleconsumerboundedqueue-classes}
38///
39/// - bdlcc::SingleProducerSingleConsumerBoundedQueue: SPSCB concurrent queue
40///
41/// # Description {#bdlcc_singleproducersingleconsumerboundedqueue-description}
42/// This component defines a type,
43/// `bdlcc::SingleProducerSingleConsumerBoundedQueue`, that provides an
44/// efficient, thread-aware bounded (capacity fixed at construction) queue of
45/// values assuming a single producer and a single consumer. The behavior of
46/// the methods `pushBack` and `tryPushBack` is undefined unless the use is by a
47/// single producer (one thread or a group of threads using external
48/// synchronization). Also, the behavior of the methods `popFront`,
49/// `tryPopFront`, and `removeAll` is undefined unless the use is by a single
50/// consumer. This class is ideal for synchronization and communication between
51/// threads in a producer-consumer model when a bounded queue is appropriate and
52/// there is only one producer thread and one consumer thread.
53///
54/// The queue provides `pushBack` and `popFront` methods for pushing data into
55/// the queue and popping data from the queue. When the queue is full, the
56/// `pushBack` methods block until data is removed from the queue. When the
57/// queue is empty, the `popFront` methods block until data appears in the
58/// queue. Non-blocking methods `tryPushBack` and `tryPopFront` are also
59/// provided. The `tryPushBack` method fails immediately, returning a non-zero
60/// value, if the queue is full. The `tryPopFront` method fails immediately,
61/// returning a non-zero value, if the queue is empty.
62///
63/// The queue may be placed into a "enqueue disabled" state using the
64/// `disablePushBack` method. When disabled, `pushBack` and `tryPushBack` fail
65/// immediately and return an error code. Any threads blocked in `pushBack`
66/// when the queue is enqueue disabled return from `pushBack` immediately and
67/// return an error code. The queue may be restored to normal operation with
68/// the `enablePushBack` method.
69///
70/// The queue may be placed into a "dequeue disabled" state using the
71/// `disablePopFront` method. When dequeue disabled, `popFront`, `tryPopFront`,
72/// and `waitUntilEmpty` fail immediately and return an error code. Any threads
73/// blocked in `popFront` and `waitUntilEmpty` when the queue is dequeue
74/// disabled return immediately and return an error code. The queue may be
75/// restored to normal operation with the `enablePopFront` method.
76///
77/// ## Template Requirements {#bdlcc_singleproducersingleconsumerboundedqueue-template-requirements}
78///
79///
80/// `bdlcc::SingleProducerSingleConsumerBoundedQueue` is a template that is
81/// parameterized on the type of element contained within the queue. The
82/// supplied template argument, `TYPE`, must provide both a default constructor
83/// and a copy constructor, as well as an assignment operator. If the default
84/// constructor accepts a `bslma::Allocator *`, `TYPE` must declare the uses
85/// `bslma::Allocator` trait (see @ref bslma_usesbslmaallocator ) so that the
86/// allocator of the queue is propagated to the elements contained in the queue.
87///
88/// ## Exception Safety {#bdlcc_singleproducersingleconsumerboundedqueue-exception-safety}
89///
90///
91/// A `bdlcc::SingleProducerSingleConsumerBoundedQueue` is exception neutral,
92/// and all of the methods of `bdlcc::SingleProducerSingleConsumerBoundedQueue`
93/// provide the strong exception safety guarantee (see @ref bsldoc_glossary ).
94///
95/// ## Move Semantics in C++03 {#bdlcc_singleproducersingleconsumerboundedqueue-move-semantics-in-c-03}
96///
97///
98/// Move-only types are supported by
99/// `bdlcc::SingleProducerSingleConsumerBoundedQueue` on C++11 platforms only
100/// (where `BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES` is defined), and are not
101/// supported on C++03 platforms. Unfortunately, in C++03, there are user types
102/// where a `bslmf::MovableRef` will not safely degrade to a lvalue reference
103/// when a move constructor is not available (types providing a constructor
104/// template taking any type), so `bslmf::MovableRefUtil::move` cannot be used
105/// directly on a user supplied template type. See internal bug report 99039150
106/// for more information.
107///
108/// ## Usage {#bdlcc_singleproducersingleconsumerboundedqueue-usage}
109///
110///
111/// This section illustrates intended use of this component.
112///
113/// ### Example 1: A Simple Thread Pool {#bdlcc_singleproducersingleconsumerboundedqueue-example-1-a-simple-thread-pool}
114///
115///
116/// In the following example a `bdlcc::SingleProducerSingleConsumerBoundedQueue`
117/// is used to communicate between a single "producer" thread and a single
118/// "consumer" thread. The "producer" will push work requests onto the queue,
119/// and the "consumer" will iteratively take a work request from the queue and
120/// service the request. This example shows a partial, simplified
121/// implementation of the `bdlmt::FixedThreadPool` class. See component
122/// @ref bdlmt_fixedthreadpool for more information.
123///
124/// First, we define a utility classes that handles a simple "work item":
125/// @code
126/// /// Work data...
127/// struct my_WorkData {
128/// };
129///
130/// struct my_WorkRequest {
131/// enum RequestType {
132/// e_WORK = 1,
133/// e_STOP = 2
134/// };
135///
136/// RequestType d_type;
137/// my_WorkData d_data;
138/// // Work data...
139/// };
140/// @endcode
141/// Next, we provide a simple function to service an individual work item. The
142/// details are unimportant for this example:
143/// @code
144/// /// Do some work based upon the specified `data`.
145/// void myDoWork(const my_WorkData& data)
146/// {
147/// // do some stuff...
148/// (void)data;
149/// }
150/// @endcode
151/// Then, we define a `myConsumer` function that will pop elements off the queue
152/// and process them. Note that the call to `queue->popFront()` will block
153/// until there is an element available on the queue:
154/// @code
155/// void myConsumer(
156/// bdlcc::SingleProducerSingleConsumerBoundedQueue<my_WorkRequest> *queue)
157/// // Pop elements from the specified `queue`.
158/// {
159/// while (1) {
160/// // `popFront()` will wait for a `my_WorkRequest` until available.
161///
162/// my_WorkRequest item;
163/// item.d_type = my_WorkRequest::e_WORK;
164///
165/// assert(0 == queue->popFront(&item));
166///
167/// if (item.d_type == my_WorkRequest::e_STOP) { break; }
168/// myDoWork(item.d_data);
169/// }
170/// }
171/// @endcode
172/// Finally, we define a `myProducer` function that serves multiple roles: it
173/// creates the `bdlcc::SingleProducerSingleConsumerBoundedQueue`, starts the
174/// consumer thread, and then produces and enqueues work items. When work
175/// requests are exhausted, this function enqueues one `e_STOP` item for the
176/// consumer queue. This `e_STOP` item indicates to the consumer thread to
177/// terminate its thread-handling function.
178/// @code
179/// /// Create a queue, start consumer thread, produce and enqueue work.
180/// void myProducer()
181/// {
182/// enum {
183/// k_MAX_QUEUE_LENGTH = 100,
184/// k_NUM_WORK_ITEMS = 1000
185/// };
186///
187/// bdlcc::SingleProducerSingleConsumerBoundedQueue<my_WorkRequest>
188/// queue(k_MAX_QUEUE_LENGTH);
189///
190/// bslmt::ThreadGroup consumerThreads;
191/// consumerThreads.addThreads(bdlf::BindUtil::bind(&myConsumer, &queue),
192/// 1);
193///
194/// for (int i = 0; i < k_NUM_WORK_ITEMS; ++i) {
195/// my_WorkRequest item;
196/// item.d_type = my_WorkRequest::e_WORK;
197/// item.d_data = my_WorkData(); // some stuff to do
198/// queue.pushBack(item);
199/// }
200///
201/// {
202/// my_WorkRequest item;
203/// item.d_type = my_WorkRequest::e_STOP;
204/// queue.pushBack(item);
205/// }
206///
207/// consumerThreads.joinAll();
208/// }
209/// @endcode
210/// @}
211/** @} */
212/** @} */
213
214/** @addtogroup bdl
215 * @{
216 */
217/** @addtogroup bdlcc
218 * @{
219 */
220/** @addtogroup bdlcc_singleproducersingleconsumerboundedqueue
221 * @{
222 */
223
224#include <bdlscm_version.h>
225
227
228#include <bslma_default.h>
230
231#include <bslmf_movableref.h>
233
234#include <bslmt_condition.h>
235#include <bslmt_lockguard.h>
236#include <bslmt_mutex.h>
237#include <bslmt_platform.h>
238#include <bslmt_threadutil.h>
239
240#include <bsls_assert.h>
243#include <bsls_objectbuffer.h>
244#include <bsls_types.h>
245
246
247namespace bdlcc {
248
249 // ===============================================================
250 // class SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard
251 // ===============================================================
252
253/// This class implements a guard that invokes `TYPE::popComplete` on a
254/// `NODE` upon destruction.
255///
256/// See @ref bdlcc_singleproducersingleconsumerboundedqueue
257template <class TYPE, class NODE>
259
260 // PRIVATE TYPES
261 typedef typename bsls::Types::Uint64 Uint64;
262
263 // DATA
264 TYPE *d_queue_p; // managed queue owning the managed node
265 NODE *d_node_p; // managed node
266 Uint64 d_index; // value of 'd_queue_p->d_popIndex'
267
268 private:
269 // NOT IMPLEMENTED
275
276 public:
277 // CREATORS
278
279 /// Create a guard managing the specified `queue` and will invoke
280 /// `popComplete` with the specified `node` and `index`.
282 NODE *node,
283 Uint64 index);
284
285 /// Destroy this object and invoke the `TYPE::popComplete`.
287};
288
289 // ==============================================
290 // class SingleProducerSingleConsumerBoundedQueue
291 // ==============================================
292
293template <class TYPE>
294#if defined(BSLS_COMPILERFEATURES_SUPPORT_ALIGNAS)
295class alignas(bslmt::Platform::e_CACHE_LINE_SIZE)
297#else
299#endif
300 // This class provides a thread-safe bounded queue of values.
301
302 // PRIVATE TYPES
303 typedef unsigned int Uint;
304 typedef typename bsls::Types::Uint64 Uint64;
305 typedef typename bsls::AtomicOperations::AtomicTypes::Uint AtomicUint;
306 typedef typename bsls::AtomicOperations::AtomicTypes::Uint64 AtomicUint64;
307 typedef typename bsls::AtomicOperations AtomicOp;
308
309 // PRIVATE CONSTANTS
310 enum {
311 // These value are used as values for `d_state` in `Node`. A node is
312 // writable at creation and after a read completes (when the single
313 // producer can write to the node). A node is readable after it is
314 // written (when the node can be read by the single consumer). The
315 // states in-between these two states (e.g., writing) are not needed by
316 // this implementation of the queue.
317
318 e_READABLE, // node can be read
319
320 e_READABLE_AND_BLOCKED, // node can be read and has blocked producer
321
322 e_WRITABLE, // node can be written
323
324 e_WRITABLE_AND_EMPTY, // node can be written, queue is empty and
325 // this is the *first* writable node
326
327 e_WRITABLE_AND_BLOCKED // node can be written, queue is empty, this
328 // is the *first* writable node, and the
329 // consumer is blocked waiting for this node
330 // to be readable
331 };
332
333 // PRIVATE TYPES
334 template <class DATA>
335 struct QueueNode {
336 // PUBLIC DATA
337 bsls::ObjectBuffer<DATA> d_value; // stored value
338 AtomicUint d_state; // `e_READABLE`, `e_WRITABLE`, etc.
339 };
340
341 typedef QueueNode<TYPE> Node;
342
343 // DATA
344 AtomicUint64 d_popIndex; // index of next element to
345 // pop
346
347 Node *d_popElement_p; // array of elements that
348 // comprise the bounded queue;
349 // identical to
350 // `d_pushElement_p`
351
352 const bsl::size_t d_popCapacity; // the capacity of the queue;
353 // identical to
354 // `d_pushCapacity`
355
356 AtomicUint d_popDisabledGeneration;
357 // generation count of pop
358 // disablements
359
360 mutable AtomicUint d_emptyCount; // count of threads in
361 // `waitUntilEmpty`
362
363 AtomicUint d_emptyGeneration; // generation count for the
364 // empty queue state, queue is
365 // empty whenever least
366 // significant bit is zero
367
368 const char d_popPad[ bslmt::Platform::e_CACHE_LINE_SIZE
369 - sizeof(AtomicUint64)
370 - sizeof(Node *)
371 - sizeof(bsl::size_t)
372 - sizeof(AtomicUint)
373 - sizeof(AtomicUint)
374 - sizeof(AtomicUint)];
375 // padding to prevent
376 // subsequent data from being
377 // in the same cache line as
378 // the prior data
379
380 AtomicUint64 d_pushIndex; // index of next target
381 // element for a push
382
383 Node *d_pushElement_p; // array of elements that
384 // comprise the bounded queue;
385 // identical to
386 // `d_popElement_p`
387
388 const bsl::size_t d_pushCapacity; // the capacity of the queue;
389 // identical to
390 // `d_popCapacity`
391
392 AtomicUint d_pushDisabledGeneration;
393 // generation count of push
394 // disablements
395
396 const char d_pushPad[ bslmt::Platform::e_CACHE_LINE_SIZE
397 - sizeof(AtomicUint64)
398 - sizeof(Node *)
399 - sizeof(bsl::size_t)
400 - sizeof(AtomicUint)];
401 // padding to prevent
402 // subsequent data from being
403 // in the same cache line as
404 // the prior data
405
406 bslmt::Mutex d_popMutex; // used with `d_popCondition`
407 // to block the consumer when
408 // the queue is empty
409
410 bslmt::Condition d_popCondition; // condition for blocking the
411 // consumer when the queue is
412 // empty
413
414 bslmt::Mutex d_pushMutex; // used with `d_pushCondition`
415 // to block the producer when
416 // the queue is full
417
418 bslmt::Condition d_pushCondition; // condition for blocking the
419 // producer when the queue is
420 // full
421
422 mutable bslmt::Mutex d_emptyMutex; // blocking point for
423 // `waitUntilEmpty`
424
425 mutable bslmt::Condition d_emptyCondition; // condition variable for
426 // `waitUntilEmpty`
427
428 bslma::Allocator *d_allocator_p; // allocator, held not owned
429
430 // FRIENDS
433 typename SingleProducerSingleConsumerBoundedQueue<TYPE>::Node>;
434
435 // PRIVATE CLASS METHODS
436
437 /// If the specified `value` does not have its lowest-order bit set to
438 /// the value of the specified `bitValue`, increment `value` until it does.
439 ///
440 /// \note Note that this method is used to modify the generation counts
441 /// stored in `d_popDisabledGeneration` and `d_pushDisabledGeneration`.
442 static void incrementUntil(AtomicUint *value, unsigned int bitValue);
443
444 // PRIVATE MANIPULATORS
445
446 /// Destruct the value stored in the specified `node`, use the specified
447 /// `index` in calculations to mark the `node` writable, unblock any
448 /// blocked "push" threads, and if the queue is empty update the empty
449 /// generation and signal the queue empty condition. This method is
450 /// used within `popFrontImp` by a guard to complete the reclamation of
451 /// a node in the presence of an exception.
452 void popComplete(Node *node, Uint64 index);
453
454 /// If the specified `isTry` is `false`, remove the element from the
455 /// front of this queue and load that element into the specified
456 /// `value`; otherwise, attempt to remove the element from the front of
457 /// this queue without blocking, and, if successful, load the `value`
458 /// with the removed element. If `false == isTry` and the queue is
459 /// empty, block until it is not empty. Return 0 on success, and a
460 /// non-zero value otherwise. Specifically, return `e_SUCCESS` on
461 /// success, `e_DISABLED` if `isPopFrontDisabled()`, `e_EMPTY` if
462 /// `true == isTry`, `!isPopFrontDisabled()`, and the queue is empty,
463 /// and `e_FAILED` if an underlying mechanism returns an error. On
464 /// failure, `value` is not changed. Threads blocked due to the queue
465 /// being empty will return `e_DISABLED` if `disablePopFront` is
466 /// invoked.
467 int popFrontImp(TYPE *value, bool isTry);
468
469 /// If the specified `isTry` is `false`, append the specified `value` to
470 /// the back of this queue; otherwise, attempt to append the `value` to
471 /// the back of this queue without blocking. Return 0 on success, and a
472 /// non-zero value otherwise. Specifically, return `e_SUCCESS` on
473 /// success, `e_DISABLED` if `isPushBackDisabled()`, `e_FULL` if
474 /// `true == isTry`, `!isPushBackDisabled()`, and the queue is full, and
475 /// `e_FAILED` if an underlying mechanism returns an error. Threads
476 /// blocked due to the queue being full will return `e_DISABLED` if
477 /// `disablePushFront` is invoked.
478 int pushBackImp(const TYPE& value, bool isTry);
479
480 /// If the specified `isTry` is `false`, append the specified
481 /// move-insertable `value` to the back of this queue; otherwise,
482 /// attempt to append the `value` to the back of this queue without
483 /// blocking. `value` is left in a valid but unspecified state. Return
484 /// 0 on success, and a non-zero value otherwise. Specifically, return
485 /// `e_DISABLED` if `isPushBackDisabled()`, `e_FULL` if `true == isTry`,
486 /// `!isPushBackDisabled()`, and the queue is full, and `e_FAILED` if an
487 /// underlying mechanism returns an error. On failure, `value` is not
488 /// changed. Threads blocked due to the queue being full will return
489 /// `e_DISABLED` if `disablePushFront` is invoked.
490 int pushBackImp(bslmf::MovableRef<TYPE> value, bool isTry);
491
492 /// Mark the specified `node` readable, signal `d_popCondition` if
493 /// necessary, and update `d_popIndex` to be the index value of the
494 /// location to be used after specified `index` location. This method
495 /// is invoked from `pushBackImp`.
496 void pushComplete(Node *node, Uint64 index);
497
498 private:
499 // NOT IMPLEMENTED
501 const SingleProducerSingleConsumerBoundedQueue&);
503 const SingleProducerSingleConsumerBoundedQueue&);
504
505 public:
506 // TRAITS
507 BSLMF_NESTED_TRAIT_DECLARATION(SingleProducerSingleConsumerBoundedQueue,
508 bslma::UsesBslmaAllocator);
509
510 // PUBLIC TYPES
511 typedef TYPE value_type; // The type for elements.
512
513 // PUBLIC CONSTANTS
514 enum {
515 e_SUCCESS = 0,
516 e_EMPTY = -1,
517 e_FULL = -2,
518 e_DISABLED = -3,
519 e_FAILED = -4
520 };
521
522 // CREATORS
523
524 /// Create a thread-aware queue with at least the specified `capacity`.
525 /// Optionally specify a `basicAllocator` used to supply memory. If
526 /// `basicAllocator` is 0, the currently installed default allocator is
527 /// used.
528 explicit
530 bsl::size_t capacity,
531 bslma::Allocator *basicAllocator = 0);
532
533 /// Destroy this object.
535
536 // MANIPULATORS
537
538 /// Remove the element from the front of this queue and load that
539 /// element into the specified `value`. If the queue is empty, block
540 /// until it is not empty. Return 0 on success, and a non-zero value
541 /// otherwise. Specifically, return `e_SUCCESS` on success,
542 /// `e_DISABLED` if `isPopFrontDisabled()` and `e_FAILED` if an
543 /// underlying mechanism returns an error. On failure, `value` is not
544 /// changed. Threads blocked due to the queue being empty will return
545 /// `e_DISABLED` if `disablePopFront` is invoked.
546 ///
547 /// \pre The behavior is undefined unless the invoker of this method is the single consumer.
548 int popFront(TYPE *value);
549
550 /// Append the specified `value` to the back of this queue. Return 0 on
551 /// success, and a non-zero value otherwise. Specifically, return
552 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()` and
553 /// `e_FAILED` if an underlying mechanism returns an error. Threads
554 /// blocked due to the queue being full will return `e_DISABLED` if `disablePushFront` is invoked.
555 ///
556 /// \pre The behavior is undefined unless the
557 /// invoker of this method is the single producer.
558 int pushBack(const TYPE& value);
559
560 /// Append the specified move-insertable `value` to the back of this
561 /// queue. `value` is left in a valid but unspecified state. Return 0
562 /// on success, and a non-zero value otherwise. Specifically, return
563 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()` and
564 /// `e_FAILED` if an underlying mechanism returns an error. On failure,
565 /// `value` is not changed. Threads blocked due to the queue being full
566 /// will return `e_DISABLED` if `disablePushFront` is invoked.
567 ///
568 /// \pre The behavior is undefined unless the invoker of this method is the
569 /// single producer.
571
572 /// Remove all items currently in this queue.
573 /// \note Note that this operation
574 /// is not atomic; if other threads are concurrently pushing items into
575 /// the queue the result of `numElements()` after this function returns is not guaranteed to be 0.
576 ///
577 /// \pre The behavior is undefined unless the
578 /// invoker of this method is the single consumer.
579 void removeAll();
580
581 /// Attempt to remove the element from the front of this queue without
582 /// blocking, and, if successful, load the specified `value` with the
583 /// removed element. Return 0 on success, and a non-zero value
584 /// otherwise. Specifically, return `e_SUCCESS` on success,
585 /// `e_DISABLED` if `isPopFrontDisabled()`, and `e_EMPTY` if
586 /// `!isPopFrontDisabled()` and the queue was empty. On failure, `value` is not changed.
587 ///
588 /// \pre The behavior is undefined unless the
589 /// invoker of this method is the single consumer.
590 int tryPopFront(TYPE *value);
591
592 /// Append the specified `value` to the back of this queue. Return 0 on
593 /// success, and a non-zero value otherwise. Specifically, return
594 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`, and
595 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full.
596 ///
597 /// \pre The behavior is undefined unless the invoker of this method is the
598 /// single producer.
599 int tryPushBack(const TYPE& value);
600
601 /// Append the specified move-insertable `value` to the back of this
602 /// queue. `value` is left in a valid but unspecified state. Return 0
603 /// on success, and a non-zero value otherwise. Specifically, return
604 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`, and
605 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full. On failure, `value` is not changed.
606 ///
607 /// \pre The behavior is undefined unless
608 /// the invoker of this method is the single producer.
610
611 // Enqueue/Dequeue State
612
613 /// Disable dequeueing from this queue. All subsequent invocations of
614 /// `popFront` or `tryPopFront` will fail immediately. If the single
615 /// consumer is blocked in `popFront`, the invocation of `popFront` will
616 /// fail immediately. Any blocked invocations of `waitUntilEmpty` will
617 /// fail immediately. If the queue is already dequeue disabled, this
618 /// method has no effect.
619 void disablePopFront();
620
621 /// Disable enqueueing into this queue. All subsequent invocations of
622 /// `pushBack` or `tryPushBack` will fail immediately. If the single
623 /// producer is blocked in `pushBack`, the invocation of `pushBack` will
624 /// fail immediately. If the queue is already enqueue disabled, this
625 /// method has no effect.
626 void disablePushBack();
627
628 /// Enable queuing. If the queue is not enqueue disabled, this call has
629 /// no effect.
630 void enablePushBack();
631
632 /// Enable dequeueing. If the queue is not dequeue disabled, this call
633 /// has no effect.
634 void enablePopFront();
635
636 // ACCESSORS
637
638 /// Return the maximum number of elements that may be stored in this queue.
639 ///
640 /// \note Note that the value returned may be greater than that
641 /// supplied at construction.
642 bsl::size_t capacity() const;
643
644 /// Return `true` if this queue is empty (has no elements), or `false`
645 /// otherwise.
646 bool isEmpty() const;
647
648 /// Return `true` if this queue is full (has no available capacity), or
649 /// `false` otherwise.
650 bool isFull() const;
651
652 /// Return `true` if this queue is dequeue disabled, and `false` otherwise.
653 ///
654 /// \note Note that the queue is created in the "dequeue enabled"
655 /// state.
656 bool isPopFrontDisabled() const;
657
658 /// Return `true` if this queue is enqueue disabled, and `false` otherwise.
659 ///
660 /// \note Note that the queue is created in the "enqueue enabled"
661 /// state.
662 bool isPushBackDisabled() const;
663
664 /// Returns the number of elements currently in this queue.
665 bsl::size_t numElements() const;
666
667 /// Block until all the elements in this queue are removed. Return 0 on
668 /// success, and a non-zero value otherwise. Specifically, return
669 /// `e_SUCCESS` on success, `e_DISABLED` if `isPopFrontDisabled()` and
670 /// `e_FAILED` if an underlying mechanism returns an error. A blocked
671 /// thread waiting for the queue to empty will return a non-zero value
672 /// if `disablePopFront` is invoked.
673 int waitUntilEmpty() const;
674
675 // Aspects
676
677 /// Return the allocator used by this object to supply memory.
679};
680
681// ============================================================================
682// INLINE DEFINITIONS
683// ============================================================================
684
685 // ---------------------------------------------------------------
686 // class SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard
687 // ---------------------------------------------------------------
688
689// CREATORS
690template <class TYPE, class NODE>
691inline
692SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard<TYPE, NODE>
693 ::SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard(
694 TYPE *queue,
695 NODE *node,
696 Uint64 index)
697: d_queue_p(queue)
698, d_node_p(node)
699, d_index(index)
700{
701}
702
703template <class TYPE, class NODE>
704inline
707{
708 d_queue_p->popComplete(d_node_p, d_index);
709}
710
711 // ----------------------------------------------
712 // class SingleProducerSingleConsumerBoundedQueue
713 // ----------------------------------------------
714
715// PRIVATE CLASS METHODS
716template <class TYPE>
718 ::incrementUntil(AtomicUint *value, unsigned int bitValue)
719{
720 unsigned int state = AtomicOp::getUintAcquire(value);
721 if (bitValue != (state & 1)) {
722 unsigned int expState;
723 do {
724 expState = state;
725 state = AtomicOp::testAndSwapUintAcqRel(value,
726 state,
727 state + 1);
728 } while (state != expState && (bitValue != (state & 1)));
729 }
730}
731
732// PRIVATE MANIPULATORS
733template <class TYPE>
734inline
735void SingleProducerSingleConsumerBoundedQueue<TYPE>::popComplete(Node *node,
736 Uint64 index)
737{
738 ++index;
739 if (index == d_popCapacity) {
740 index = 0;
741 }
742 AtomicOp::setUint64Release(&d_popIndex, index);
743
744 node->d_value.object().~TYPE();
745
746 Uint nodeState = AtomicOp::swapUintAcqRel(&node->d_state, e_WRITABLE);
747 if (e_READABLE_AND_BLOCKED == nodeState) {
748 {
749 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
750 }
751 d_pushCondition.signal();
752 }
753
754 // If the node subsequent to 'node' is writable, the queue is empty and the
755 // node subsequent to 'node' must be marked as 'e_WRITABLE_AND_EMPTY'.
756
757 nodeState = AtomicOp::testAndSwapUintAcqRel(&d_popElement_p[index].d_state,
758 e_WRITABLE,
759 e_WRITABLE_AND_EMPTY);
760 if (e_WRITABLE == nodeState) {
761 // The queue is empty, increment the empty generation count.
762
763 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
764 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
765 {
766 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
767 }
768 d_emptyCondition.broadcast();
769 }
770 }
771}
772
773template <class TYPE>
774int SingleProducerSingleConsumerBoundedQueue<TYPE>::popFrontImp(TYPE *value,
775 bool isTry)
776{
777 Uint64 index = AtomicOp::getUint64Acquire(&d_popIndex);
778 const Uint disabledGen =
779 AtomicOp::getUintAcquire(&d_popDisabledGeneration);
780
781 if (disabledGen & 1) {
782 return e_DISABLED; // RETURN
783 }
784
785 Node& node = d_popElement_p[index];
786
787 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
788
789 // If the node is not available for reading:
790 // * if this is a "try" invocation, return
791 // * otherwise, yield and check again, then block
792 // Note that 'e_WRITABLE_AND_BLOCKED != nodeState' since this is the one
793 // consumer.
794
795 if (e_WRITABLE_AND_EMPTY == nodeState) {
796 if (isTry) {
797 return e_EMPTY; // RETURN
798 }
799
801 nodeState = AtomicOp::getUintAcquire(&node.d_state);
802 if (e_WRITABLE_AND_EMPTY == nodeState) {
803 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
804
805 nodeState = AtomicOp::testAndSwapUintAcqRel(
806 &node.d_state,
807 nodeState,
808 e_WRITABLE_AND_BLOCKED);
809
810 while (( e_WRITABLE_AND_EMPTY == nodeState
811 || e_WRITABLE_AND_BLOCKED == nodeState)
812 && disabledGen ==
813 AtomicOp::getUintAcquire(&d_popDisabledGeneration)) {
814 int rv = d_popCondition.wait(&d_popMutex);
815 if (rv) {
816 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
817 e_WRITABLE_AND_BLOCKED,
818 e_WRITABLE_AND_EMPTY);
819 return e_FAILED; // RETURN
820 }
821 nodeState = AtomicOp::getUint(&node.d_state);
822 }
823
824 // The following checks for disablement being the cause of exiting
825 // the 'while' loop.
826
827 if ( e_WRITABLE_AND_EMPTY == nodeState
828 || e_WRITABLE_AND_BLOCKED == nodeState) {
829 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
830 e_WRITABLE_AND_BLOCKED,
831 e_WRITABLE_AND_EMPTY);
832 return e_DISABLED; // RETURN
833 }
834 }
835 }
836
837 SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard<
838 SingleProducerSingleConsumerBoundedQueue<TYPE>, Node>
839 guard(this, &node, index);
840
841#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
842 *value = bslmf::MovableRefUtil::move(node.d_value.object());
843#else
844 *value = node.d_value.object();
845#endif
846
847 return e_SUCCESS;
848}
849
850template <class TYPE>
851int SingleProducerSingleConsumerBoundedQueue<TYPE>::pushBackImp(
852 const TYPE& value,
853 bool isTry)
854{
855 Uint64 index = AtomicOp::getUint64Acquire(&d_pushIndex);
856 const Uint disabledGen =
857 AtomicOp::getUintAcquire(&d_pushDisabledGeneration);
858
859 if (disabledGen & 1) {
860 return e_DISABLED; // RETURN
861 }
862
863 Node& node = d_pushElement_p[index];
864
865 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
866
867 // If the node is not available for writing:
868 // * if this is a "try" invocation, return
869 // * otherwise, yield and check again, then block
870 // Note that 'e_READABLE_AND_BLOCKED != nodeState' since this is the one
871 // producer.
872
873 if (e_READABLE == nodeState) {
874 if (isTry) {
875 return e_FULL; // RETURN
876 }
877
879 nodeState = AtomicOp::getUintAcquire(&node.d_state);
880 if (e_READABLE == nodeState) {
881 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
882
883 nodeState = AtomicOp::testAndSwapUintAcqRel(
884 &node.d_state,
885 e_READABLE,
886 e_READABLE_AND_BLOCKED);
887
888 while (( e_READABLE == nodeState
889 || e_READABLE_AND_BLOCKED == nodeState)
890 && disabledGen ==
891 AtomicOp::getUintAcquire(&d_pushDisabledGeneration)) {
892 int rv = d_pushCondition.wait(&d_pushMutex);
893 if (rv) {
894 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
895 e_READABLE_AND_BLOCKED,
896 e_READABLE);
897 return e_FAILED; // RETURN
898 }
899 nodeState = AtomicOp::getUint(&node.d_state);
900 }
901
902 // The following checks for disablement being the cause of exiting
903 // the 'while' loop.
904
905 if ( e_READABLE == nodeState
906 || e_READABLE_AND_BLOCKED == nodeState) {
907 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
908 e_READABLE_AND_BLOCKED,
909 e_READABLE);
910 return e_DISABLED; // RETURN
911 }
912 }
913 }
914
915 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
916 value,
917 d_allocator_p);
918
919 pushComplete(&node, index);
920
921 return e_SUCCESS;
922}
923
924template <class TYPE>
925int SingleProducerSingleConsumerBoundedQueue<TYPE>::pushBackImp(
927 bool isTry)
928{
929 Uint64 index = AtomicOp::getUint64Acquire(&d_pushIndex);
930 const Uint disabledGen =
931 AtomicOp::getUintAcquire(&d_pushDisabledGeneration);
932
933 if (disabledGen & 1) {
934 return e_DISABLED; // RETURN
935 }
936
937 Node& node = d_pushElement_p[index];
938
939 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
940
941 if (e_READABLE == nodeState) {
942 if (isTry) {
943 return e_FULL; // RETURN
944 }
945
947 nodeState = AtomicOp::getUintAcquire(&node.d_state);
948 if (e_READABLE == nodeState) {
949 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
950
951 nodeState = AtomicOp::testAndSwapUintAcqRel(
952 &node.d_state,
953 e_READABLE,
954 e_READABLE_AND_BLOCKED);
955
956 while (( e_READABLE == nodeState
957 || e_READABLE_AND_BLOCKED == nodeState)
958 && disabledGen ==
959 AtomicOp::getUintAcquire(&d_pushDisabledGeneration)) {
960 int rv = d_pushCondition.wait(&d_pushMutex);
961 if (rv) {
962 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
963 e_READABLE_AND_BLOCKED,
964 e_READABLE);
965 return e_FAILED; // RETURN
966 }
967 nodeState = AtomicOp::getUint(&node.d_state);
968 }
969
970 if ( e_READABLE == nodeState
971 || e_READABLE_AND_BLOCKED == nodeState) {
972 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
973 e_READABLE_AND_BLOCKED,
974 e_READABLE);
975 return e_DISABLED; // RETURN
976 }
977 }
978 }
979
980 TYPE& dummy = value;
981 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
982 dummy,
983 d_allocator_p);
984
985 pushComplete(&node, index);
986
987 return e_SUCCESS;
988}
989
990template <class TYPE>
991inline
992void SingleProducerSingleConsumerBoundedQueue<TYPE>::pushComplete(
993 Node *node,
994 Uint64 index)
995{
996
997 Uint nodeState = AtomicOp::swapUintAcqRel(&node->d_state, e_READABLE);
998 if (e_WRITABLE_AND_BLOCKED == nodeState) {
999 // Queue is no longer empty and the consumer is blocked.
1000
1001 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
1002 {
1003 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
1004 }
1005 d_popCondition.signal();
1006 }
1007 else if (e_WRITABLE_AND_EMPTY == nodeState) {
1008 // Queue is no longer empty.
1009
1010 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
1011 }
1012
1013 ++index;
1014 if (index == d_pushCapacity) {
1015 index = 0;
1016 }
1017 AtomicOp::setUint64Release(&d_pushIndex, index);
1018}
1019
1020// CREATORS
1021template <class TYPE>
1024 bslma::Allocator *basicAllocator)
1025: d_popElement_p(0)
1026, d_popCapacity(capacity > 0 ? capacity : 1)
1027, d_popPad()
1028, d_pushCapacity(capacity > 0 ? capacity : 1)
1029, d_pushPad()
1030, d_popMutex()
1031, d_popCondition()
1032, d_pushMutex()
1033, d_pushCondition()
1034, d_emptyMutex()
1035, d_emptyCondition()
1036, d_allocator_p(bslma::Default::allocator(basicAllocator))
1037{
1038 AtomicOp::initUint64(&d_popIndex, 0);
1039 AtomicOp::initUint64(&d_pushIndex, 0);
1040
1041 AtomicOp::initUint(&d_popDisabledGeneration, 0);
1042 AtomicOp::initUint(&d_emptyCount, 0);
1043 AtomicOp::initUint(&d_emptyGeneration, 0);
1044 AtomicOp::initUint(&d_pushDisabledGeneration, 0);
1045
1046 d_popElement_p = static_cast<Node *>(
1047 d_allocator_p->allocate(d_popCapacity * sizeof(Node)));
1048
1049 d_pushElement_p = d_popElement_p;
1050
1051 AtomicOp::initUint(&d_popElement_p[0].d_state, e_WRITABLE_AND_EMPTY);
1052 for (bsl::size_t i = 1; i < d_popCapacity; ++i) {
1053 AtomicOp::initUint(&d_popElement_p[i].d_state, e_WRITABLE);
1054 }
1055}
1056
1057template <class TYPE>
1060{
1061 if (d_popElement_p) {
1062 removeAll();
1063 d_allocator_p->deallocate(d_popElement_p);
1064 }
1065}
1066
1067// MANIPULATORS
1068template <class TYPE>
1069inline
1071{
1072 return popFrontImp(value, false);
1073}
1074
1075template <class TYPE>
1076inline
1078{
1079 return pushBackImp(value, false);
1080}
1081
1082template <class TYPE>
1083inline
1089
1090template <class TYPE>
1092{
1093 Uint64 index = AtomicOp::getUint64Acquire(&d_popIndex);
1094 Uint nodeState = AtomicOp::getUintAcquire(
1095 &d_popElement_p[index].d_state);
1096
1097 while (e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState) {
1098 d_popElement_p[index].d_value.object().~TYPE();
1099
1100 AtomicOp::swapUintAcqRel(&d_popElement_p[index].d_state, e_WRITABLE);
1101
1102 ++index;
1103 if (index == d_popCapacity) {
1104 index = 0;
1105 }
1106
1107 nodeState = AtomicOp::getUintAcquire(&d_popElement_p[index].d_state);
1108 }
1109
1110 // If the node subsequent to the last removed element is writable, the
1111 // queue is empty and the node subsequent to the last removed element' must
1112 // be marked as 'e_WRITABLE_AND_EMPTY'.
1113
1114 nodeState = AtomicOp::testAndSwapUintAcqRel(&d_popElement_p[index].d_state,
1115 e_WRITABLE,
1116 e_WRITABLE_AND_EMPTY);
1117
1118 if (e_WRITABLE == nodeState) {
1119 // The queue is empty, increment the empty generation count.
1120
1121 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
1122 }
1123 else {
1124 // A 'removeAll' makes the queue empty. Since there has been a
1125 // 'pushBack' before the queue could be marked empty ('e_WRITABLE !=
1126 // nodeState'), increase the empty generation by 2 to note the queue
1127 // was empty at some point during this method call but is no longer
1128 // empty (1 for becoming empty, 1 for leaving the empty state).
1129
1130 AtomicOp::addUintAcqRel(&d_emptyGeneration, 2);
1131 }
1132
1133 AtomicOp::setUint64Release(&d_popIndex, index);
1134
1135 {
1136 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
1137 }
1138 d_pushCondition.signal();
1139
1140 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
1141 {
1142 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1143 }
1144 d_emptyCondition.broadcast();
1145 }
1146}
1147
1148template <class TYPE>
1149inline
1151{
1152 return popFrontImp(value, true);
1153}
1154
1155template <class TYPE>
1156inline
1158 const TYPE& value)
1159{
1160 return pushBackImp(value, true);
1161}
1162
1163template <class TYPE>
1164inline
1170
1171 // Enqueue/Dequeue State
1172
1173template <class TYPE>
1174inline
1176{
1177 incrementUntil(&d_popDisabledGeneration, 1);
1178
1179 {
1180 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
1181 }
1182 d_popCondition.broadcast();
1183
1184 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
1185 {
1186 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1187 }
1188 d_emptyCondition.broadcast();
1189 }
1190}
1191
1192template <class TYPE>
1193inline
1195{
1196 incrementUntil(&d_pushDisabledGeneration, 1);
1197
1198 {
1199 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
1200 }
1201 d_pushCondition.broadcast();
1202}
1203
1204template <class TYPE>
1205inline
1207{
1208 incrementUntil(&d_popDisabledGeneration, 0);
1209}
1210
1211template <class TYPE>
1212inline
1214{
1215 incrementUntil(&d_pushDisabledGeneration, 0);
1216}
1217
1218// ACCESSORS
1219template <class TYPE>
1220inline
1222{
1223 return d_popCapacity;
1224}
1225
1226template <class TYPE>
1227inline
1229{
1230 return 0 == (AtomicOp::getUintAcquire(&d_emptyGeneration) & 1);
1231}
1232
1233template <class TYPE>
1234inline
1236{
1237 Node& node = d_pushElement_p[AtomicOp::getUint64Acquire(
1238 &d_pushIndex)];
1239 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
1240
1241 return e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState;
1242}
1243
1244template <class TYPE>
1245inline
1247{
1248 return 1 == (AtomicOp::getUintAcquire(&d_popDisabledGeneration) & 1);
1249}
1250
1251template <class TYPE>
1252inline
1254{
1255 return 1 == (AtomicOp::getUintAcquire(&d_pushDisabledGeneration) & 1);
1256}
1257
1258template <class TYPE>
1259inline
1261{
1262 Uint64 popIndex = AtomicOp::getUint64Acquire(&d_popIndex);
1263 Uint64 pushIndex = AtomicOp::getUint64Acquire(&d_pushIndex);
1264 Node& node = d_pushElement_p[pushIndex];
1265 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
1266
1267 if (e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState) {
1268 return d_popCapacity; // RETURN
1269 }
1270
1271 return static_cast<bsl::size_t>( pushIndex >= popIndex
1272 ? pushIndex - popIndex
1273 : pushIndex + d_popCapacity - popIndex);
1274}
1275
1276template <class TYPE>
1278{
1279 AtomicOp::addUintAcqRel(&d_emptyCount, 1);
1280
1281 const Uint initEmptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1282
1283 const Uint disabledGen =
1284 AtomicOp::getUintAcquire(&d_popDisabledGeneration);
1285
1286 if (disabledGen & 1) {
1287 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1288 return e_DISABLED; // RETURN
1289 }
1290
1291 if (0 == (initEmptyGen & 1)) {
1292 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1293 return e_SUCCESS; // RETURN
1294 }
1295
1296 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1297
1298 Uint emptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1299
1300 while ( initEmptyGen == emptyGen
1301 && disabledGen ==
1302 AtomicOp::getUintAcquire(&d_popDisabledGeneration)) {
1303 int rv = d_emptyCondition.wait(&d_emptyMutex);
1304 if (rv) {
1305 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1306 return e_FAILED; // RETURN
1307 }
1308 emptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1309 }
1310
1311 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1312
1313 if (initEmptyGen == emptyGen) {
1314 return e_DISABLED; // RETURN
1315 }
1316
1317 return e_SUCCESS;
1318}
1319
1320 // Aspects
1321
1322template <class TYPE>
1323inline
1325 const
1326{
1327 return d_allocator_p;
1328}
1329
1330} // close package namespace
1331
1332
1333#endif
1334
1335// ----------------------------------------------------------------------------
1336// Copyright 2019 Bloomberg Finance L.P.
1337//
1338// Licensed under the Apache License, Version 2.0 (the "License");
1339// you may not use this file except in compliance with the License.
1340// You may obtain a copy of the License at
1341//
1342// http://www.apache.org/licenses/LICENSE-2.0
1343//
1344// Unless required by applicable law or agreed to in writing, software
1345// distributed under the License is distributed on an "AS IS" BASIS,
1346// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1347// See the License for the specific language governing permissions and
1348// limitations under the License.
1349// ----------------------------- END-OF-FILE ----------------------------------
1350
1351/** @} */
1352/** @} */
1353/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:258
~SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard()
Destroy this object and invoke the TYPE::popComplete.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:706
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:298
void removeAll()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1091
int waitUntilEmpty() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1277
bool isPushBackDisabled() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1253
bsl::size_t capacity() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1221
void disablePopFront()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1175
int tryPushBack(const TYPE &value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1157
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1324
int pushBack(const TYPE &value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1077
bool isPopFrontDisabled() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1246
TYPE value_type
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:511
bool isEmpty() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1228
~SingleProducerSingleConsumerBoundedQueue()
Destroy this object.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1059
void enablePushBack()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1213
bool isFull() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1235
void disablePushBack()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1194
bsl::size_t numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1260
int tryPopFront(TYPE *value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1150
int popFront(TYPE *value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1070
void enablePopFront()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1206
Definition bslma_allocator.h:545
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:752
Definition bslmt_condition.h:220
Definition bslmt_lockguard.h:234
Definition bslmt_mutex.h:317
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlcc_boundedqueue.h:270
Definition baljsn_encoder_testtypes.h:76
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1660
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1617
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
@ e_CACHE_LINE_SIZE
Definition bslmt_platform.h:214
static void yield()
Definition bslmt_threadutil.h:1100
Definition bsls_atomicoperations.h:836
static void initUint64(AtomicTypes::Uint64 *atomicUint, Types::Uint64 initialValue=0)
Definition bsls_atomicoperations.h:2123
static void initUint(AtomicTypes::Uint *atomicUint, unsigned int initialValue=0)
Definition bsls_atomicoperations.h:1924
unsigned long long Uint64
Definition bsls_types.h:139
Definition bsls_objectbuffer.h:277