BDE 4.14.0 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 // NOT IMPLEMENTED
274
275 public:
276 // CREATORS
277
278 /// Create a guard managing the specified `queue` and will invoke
279 /// `popComplete` with the specified `node` and `index`.
281 NODE *node,
282 Uint64 index);
283
284 /// Destroy this object and invoke the `TYPE::popComplete`.
286};
287
288 // ==============================================
289 // class SingleProducerSingleConsumerBoundedQueue
290 // ==============================================
291
292template <class TYPE>
293#if defined(BSLS_COMPILERFEATURES_SUPPORT_ALIGNAS)
294class alignas(bslmt::Platform::e_CACHE_LINE_SIZE)
296#else
298#endif
299 // This class provides a thread-safe bounded queue of values.
300
301 // PRIVATE TYPES
302 typedef unsigned int Uint;
303 typedef typename bsls::Types::Uint64 Uint64;
304 typedef typename bsls::AtomicOperations::AtomicTypes::Uint AtomicUint;
305 typedef typename bsls::AtomicOperations::AtomicTypes::Uint64 AtomicUint64;
306 typedef typename bsls::AtomicOperations AtomicOp;
307
308 // PRIVATE CONSTANTS
309 enum {
310 // These value are used as values for `d_state` in `Node`. A node is
311 // writable at creation and after a read completes (when the single
312 // producer can write to the node). A node is readable after it is
313 // written (when the node can be read by the single consumer). The
314 // states in-between these two states (e.g., writing) are not needed by
315 // this implementation of the queue.
316
317 e_READABLE, // node can be read
318
319 e_READABLE_AND_BLOCKED, // node can be read and has blocked producer
320
321 e_WRITABLE, // node can be written
322
323 e_WRITABLE_AND_EMPTY, // node can be written, queue is empty and
324 // this is the *first* writable node
325
326 e_WRITABLE_AND_BLOCKED // node can be written, queue is empty, this
327 // is the *first* writable node, and the
328 // consumer is blocked waiting for this node
329 // to be readable
330 };
331
332 // PRIVATE TYPES
333 template <class DATA>
334 struct QueueNode {
335 // PUBLIC DATA
336 bsls::ObjectBuffer<DATA> d_value; // stored value
337 AtomicUint d_state; // `e_READABLE`, `e_WRITABLE`, etc.
338 };
339
340 typedef QueueNode<TYPE> Node;
341
342 // DATA
343 AtomicUint64 d_popIndex; // index of next element to
344 // pop
345
346 Node *d_popElement_p; // array of elements that
347 // comprise the bounded queue;
348 // identical to
349 // `d_pushElement_p`
350
351 const bsl::size_t d_popCapacity; // the capacity of the queue;
352 // identical to
353 // `d_pushCapacity`
354
355 AtomicUint d_popDisabledGeneration;
356 // generation count of pop
357 // disablements
358
359 mutable AtomicUint d_emptyCount; // count of threads in
360 // `waitUntilEmpty`
361
362 AtomicUint d_emptyGeneration; // generation count for the
363 // empty queue state, queue is
364 // empty whenever least
365 // significant bit is zero
366
367 const char d_popPad[ bslmt::Platform::e_CACHE_LINE_SIZE
368 - sizeof(AtomicUint64)
369 - sizeof(Node *)
370 - sizeof(bsl::size_t)
371 - sizeof(AtomicUint)
372 - sizeof(AtomicUint)
373 - sizeof(AtomicUint)];
374 // padding to prevent
375 // subsequent data from being
376 // in the same cache line as
377 // the prior data
378
379 AtomicUint64 d_pushIndex; // index of next target
380 // element for a push
381
382 Node *d_pushElement_p; // array of elements that
383 // comprise the bounded queue;
384 // identical to
385 // `d_popElement_p`
386
387 const bsl::size_t d_pushCapacity; // the capacity of the queue;
388 // identical to
389 // `d_popCapacity`
390
391 AtomicUint d_pushDisabledGeneration;
392 // generation count of push
393 // disablements
394
395 const char d_pushPad[ bslmt::Platform::e_CACHE_LINE_SIZE
396 - sizeof(AtomicUint64)
397 - sizeof(Node *)
398 - sizeof(bsl::size_t)
399 - sizeof(AtomicUint)];
400 // padding to prevent
401 // subsequent data from being
402 // in the same cache line as
403 // the prior data
404
405 bslmt::Mutex d_popMutex; // used with `d_popCondition`
406 // to block the consumer when
407 // the queue is empty
408
409 bslmt::Condition d_popCondition; // condition for blocking the
410 // consumer when the queue is
411 // empty
412
413 bslmt::Mutex d_pushMutex; // used with `d_pushCondition`
414 // to block the producer when
415 // the queue is full
416
417 bslmt::Condition d_pushCondition; // condition for blocking the
418 // producer when the queue is
419 // full
420
421 mutable bslmt::Mutex d_emptyMutex; // blocking point for
422 // `waitUntilEmpty`
423
424 mutable bslmt::Condition d_emptyCondition; // condition variable for
425 // `waitUntilEmpty`
426
427 bslma::Allocator *d_allocator_p; // allocator, held not owned
428
429 // FRIENDS
432 typename SingleProducerSingleConsumerBoundedQueue<TYPE>::Node>;
433
434 // PRIVATE CLASS METHODS
435
436 /// If the specified `value` does not have its lowest-order bit set to
437 /// the value of the specified `bitValue`, increment `value` until it
438 /// does. Note that this method is used to modify the generation counts
439 /// stored in `d_popDisabledGeneration` and `d_pushDisabledGeneration`.
440 static void incrementUntil(AtomicUint *value, unsigned int bitValue);
441
442 // PRIVATE MANIPULATORS
443
444 /// Destruct the value stored in the specified `node`, use the specified
445 /// `index` in calculations to mark the `node` writable, unblock any
446 /// blocked "push" threads, and if the queue is empty update the empty
447 /// generation and signal the queue empty condition. This method is
448 /// used within `popFrontImp` by a guard to complete the reclamation of
449 /// a node in the presence of an exception.
450 void popComplete(Node *node, Uint64 index);
451
452 /// If the specified `isTry` is `false`, remove the element from the
453 /// front of this queue and load that element into the specified
454 /// `value`; otherwise, attempt to remove the element from the front of
455 /// this queue without blocking, and, if successful, load the `value`
456 /// with the removed element. If `false == isTry` and the queue is
457 /// empty, block until it is not empty. Return 0 on success, and a
458 /// non-zero value otherwise. Specifically, return `e_SUCCESS` on
459 /// success, `e_DISABLED` if `isPopFrontDisabled()`, `e_EMPTY` if
460 /// `true == isTry`, `!isPopFrontDisabled()`, and the queue is empty,
461 /// and `e_FAILED` if an underlying mechanism returns an error. On
462 /// failure, `value` is not changed. Threads blocked due to the queue
463 /// being empty will return `e_DISABLED` if `disablePopFront` is
464 /// invoked.
465 int popFrontImp(TYPE *value, bool isTry);
466
467 /// If the specified `isTry` is `false`, append the specified `value` to
468 /// the back of this queue; otherwise, attempt to append the `value` to
469 /// the back of this queue without blocking. Return 0 on success, and a
470 /// non-zero value otherwise. Specifically, return `e_SUCCESS` on
471 /// success, `e_DISABLED` if `isPushBackDisabled()`, `e_FULL` if
472 /// `true == isTry`, `!isPushBackDisabled()`, and the queue is full, and
473 /// `e_FAILED` if an underlying mechanism returns an error. Threads
474 /// blocked due to the queue being full will return `e_DISABLED` if
475 /// `disablePushFront` is invoked.
476 int pushBackImp(const TYPE& value, bool isTry);
477
478 /// If the specified `isTry` is `false`, append the specified
479 /// move-insertable `value` to the back of this queue; otherwise,
480 /// attempt to append the `value` to the back of this queue without
481 /// blocking. `value` is left in a valid but unspecified state. Return
482 /// 0 on success, and a non-zero value otherwise. Specifically, return
483 /// `e_DISABLED` if `isPushBackDisabled()`, `e_FULL` if `true == isTry`,
484 /// `!isPushBackDisabled()`, and the queue is full, and `e_FAILED` if an
485 /// underlying mechanism returns an error. On failure, `value` is not
486 /// changed. Threads blocked due to the queue being full will return
487 /// `e_DISABLED` if `disablePushFront` is invoked.
488 int pushBackImp(bslmf::MovableRef<TYPE> value, bool isTry);
489
490 /// Mark the specified `node` readable, signal `d_popCondition` if
491 /// necessary, and update `d_popIndex` to be the index value of the
492 /// location to be used after specified `index` location. This method
493 /// is invoked from `pushBackImp`.
494 void pushComplete(Node *node, Uint64 index);
495
496 // NOT IMPLEMENTED
498 const SingleProducerSingleConsumerBoundedQueue&);
500 const SingleProducerSingleConsumerBoundedQueue&);
501
502 public:
503 // TRAITS
504 BSLMF_NESTED_TRAIT_DECLARATION(SingleProducerSingleConsumerBoundedQueue,
505 bslma::UsesBslmaAllocator);
506
507 // PUBLIC TYPES
508 typedef TYPE value_type; // The type for elements.
509
510 // PUBLIC CONSTANTS
511 enum {
512 e_SUCCESS = 0,
513 e_EMPTY = -1,
514 e_FULL = -2,
515 e_DISABLED = -3,
516 e_FAILED = -4
517 };
518
519 // CREATORS
520
521 /// Create a thread-aware queue with at least the specified `capacity`.
522 /// Optionally specify a `basicAllocator` used to supply memory. If
523 /// `basicAllocator` is 0, the currently installed default allocator is
524 /// used.
525 explicit
527 bsl::size_t capacity,
528 bslma::Allocator *basicAllocator = 0);
529
530 /// Destroy this object.
532
533 // MANIPULATORS
534
535 /// Remove the element from the front of this queue and load that
536 /// element into the specified `value`. If the queue is empty, block
537 /// until it is not empty. Return 0 on success, and a non-zero value
538 /// otherwise. Specifically, return `e_SUCCESS` on success,
539 /// `e_DISABLED` if `isPopFrontDisabled()` and `e_FAILED` if an
540 /// underlying mechanism returns an error. On failure, `value` is not
541 /// changed. Threads blocked due to the queue being empty will return
542 /// `e_DISABLED` if `disablePopFront` is invoked. The behavior is
543 /// undefined unless the invoker of this method is the single consumer.
544 int popFront(TYPE *value);
545
546 /// Append the specified `value` to the back of this queue. Return 0 on
547 /// success, and a non-zero value otherwise. Specifically, return
548 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()` and
549 /// `e_FAILED` if an underlying mechanism returns an error. Threads
550 /// blocked due to the queue being full will return `e_DISABLED` if
551 /// `disablePushFront` is invoked. The behavior is undefined unless the
552 /// invoker of this method is the single producer.
553 int pushBack(const TYPE& value);
554
555 /// Append the specified move-insertable `value` to the back of this
556 /// queue. `value` is left in a valid but unspecified state. Return 0
557 /// on success, and a non-zero value otherwise. Specifically, return
558 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()` and
559 /// `e_FAILED` if an underlying mechanism returns an error. On failure,
560 /// `value` is not changed. Threads blocked due to the queue being full
561 /// will return `e_DISABLED` if `disablePushFront` is invoked. The
562 /// behavior is undefined unless the invoker of this method is the
563 /// single producer.
565
566 /// Remove all items currently in this queue. Note that this operation
567 /// is not atomic; if other threads are concurrently pushing items into
568 /// the queue the result of `numElements()` after this function returns
569 /// is not guaranteed to be 0. The behavior is undefined unless the
570 /// invoker of this method is the single consumer.
571 void removeAll();
572
573 /// Attempt to remove the element from the front of this queue without
574 /// blocking, and, if successful, load the specified `value` with the
575 /// removed element. Return 0 on success, and a non-zero value
576 /// otherwise. Specifically, return `e_SUCCESS` on success,
577 /// `e_DISABLED` if `isPopFrontDisabled()`, and `e_EMPTY` if
578 /// `!isPopFrontDisabled()` and the queue was empty. On failure,
579 /// `value` is not changed. The behavior is undefined unless the
580 /// invoker of this method is the single consumer.
581 int tryPopFront(TYPE *value);
582
583 /// Append the specified `value` to the back of this queue. Return 0 on
584 /// success, and a non-zero value otherwise. Specifically, return
585 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`, and
586 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full. The
587 /// behavior is undefined unless the invoker of this method is the
588 /// single producer.
589 int tryPushBack(const TYPE& value);
590
591 /// Append the specified move-insertable `value` to the back of this
592 /// queue. `value` is left in a valid but unspecified state. Return 0
593 /// on 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. On
596 /// failure, `value` is not changed. The behavior is undefined unless
597 /// the invoker of this method is the single producer.
599
600 // Enqueue/Dequeue State
601
602 /// Disable dequeueing from this queue. All subsequent invocations of
603 /// `popFront` or `tryPopFront` will fail immediately. If the single
604 /// consumer is blocked in `popFront`, the invocation of `popFront` will
605 /// fail immediately. Any blocked invocations of `waitUntilEmpty` will
606 /// fail immediately. If the queue is already dequeue disabled, this
607 /// method has no effect.
608 void disablePopFront();
609
610 /// Disable enqueueing into this queue. All subsequent invocations of
611 /// `pushBack` or `tryPushBack` will fail immediately. If the single
612 /// producer is blocked in `pushBack`, the invocation of `pushBack` will
613 /// fail immediately. If the queue is already enqueue disabled, this
614 /// method has no effect.
615 void disablePushBack();
616
617 /// Enable queuing. If the queue is not enqueue disabled, this call has
618 /// no effect.
619 void enablePushBack();
620
621 /// Enable dequeueing. If the queue is not dequeue disabled, this call
622 /// has no effect.
623 void enablePopFront();
624
625 // ACCESSORS
626
627 /// Return the maximum number of elements that may be stored in this
628 /// queue. Note that the value returned may be greater than that
629 /// supplied at construction.
630 bsl::size_t capacity() const;
631
632 /// Return `true` if this queue is empty (has no elements), or `false`
633 /// otherwise.
634 bool isEmpty() const;
635
636 /// Return `true` if this queue is full (has no available capacity), or
637 /// `false` otherwise.
638 bool isFull() const;
639
640 /// Return `true` if this queue is dequeue disabled, and `false`
641 /// otherwise. Note that the queue is created in the "dequeue enabled"
642 /// state.
643 bool isPopFrontDisabled() const;
644
645 /// Return `true` if this queue is enqueue disabled, and `false`
646 /// otherwise. Note that the queue is created in the "enqueue enabled"
647 /// state.
648 bool isPushBackDisabled() const;
649
650 /// Returns the number of elements currently in this queue.
651 bsl::size_t numElements() const;
652
653 /// Block until all the elements in this queue are removed. Return 0 on
654 /// success, and a non-zero value otherwise. Specifically, return
655 /// `e_SUCCESS` on success, `e_DISABLED` if `isPopFrontDisabled()` and
656 /// `e_FAILED` if an underlying mechanism returns an error. A blocked
657 /// thread waiting for the queue to empty will return a non-zero value
658 /// if `disablePopFront` is invoked.
659 int waitUntilEmpty() const;
660
661 // Aspects
662
663 /// Return the allocator used by this object to supply memory.
665};
666
667// ============================================================================
668// INLINE DEFINITIONS
669// ============================================================================
670
671 // ---------------------------------------------------------------
672 // class SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard
673 // ---------------------------------------------------------------
674
675// CREATORS
676template <class TYPE, class NODE>
677inline
678SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard<TYPE, NODE>
679 ::SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard(
680 TYPE *queue,
681 NODE *node,
682 Uint64 index)
683: d_queue_p(queue)
684, d_node_p(node)
685, d_index(index)
686{
687}
688
689template <class TYPE, class NODE>
690inline
693{
694 d_queue_p->popComplete(d_node_p, d_index);
695}
696
697 // ----------------------------------------------
698 // class SingleProducerSingleConsumerBoundedQueue
699 // ----------------------------------------------
700
701// PRIVATE CLASS METHODS
702template <class TYPE>
704 ::incrementUntil(AtomicUint *value, unsigned int bitValue)
705{
706 unsigned int state = AtomicOp::getUintAcquire(value);
707 if (bitValue != (state & 1)) {
708 unsigned int expState;
709 do {
710 expState = state;
711 state = AtomicOp::testAndSwapUintAcqRel(value,
712 state,
713 state + 1);
714 } while (state != expState && (bitValue == (state & 1)));
715 }
716}
717
718// PRIVATE MANIPULATORS
719template <class TYPE>
720inline
721void SingleProducerSingleConsumerBoundedQueue<TYPE>::popComplete(Node *node,
722 Uint64 index)
723{
724 ++index;
725 if (index == d_popCapacity) {
726 index = 0;
727 }
728 AtomicOp::setUint64Release(&d_popIndex, index);
729
730 node->d_value.object().~TYPE();
731
732 Uint nodeState = AtomicOp::swapUintAcqRel(&node->d_state, e_WRITABLE);
733 if (e_READABLE_AND_BLOCKED == nodeState) {
734 {
735 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
736 }
737 d_pushCondition.signal();
738 }
739
740 // If the node subsequent to 'node' is writable, the queue is empty and the
741 // node subsequent to 'node' must be marked as 'e_WRITABLE_AND_EMPTY'.
742
743 nodeState = AtomicOp::testAndSwapUintAcqRel(&d_popElement_p[index].d_state,
744 e_WRITABLE,
745 e_WRITABLE_AND_EMPTY);
746 if (e_WRITABLE == nodeState) {
747 // The queue is empty, increment the empty generation count.
748
749 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
750 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
751 {
752 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
753 }
754 d_emptyCondition.broadcast();
755 }
756 }
757}
758
759template <class TYPE>
760int SingleProducerSingleConsumerBoundedQueue<TYPE>::popFrontImp(TYPE *value,
761 bool isTry)
762{
763 Uint64 index = AtomicOp::getUint64Acquire(&d_popIndex);
764 const Uint disabledGen =
765 AtomicOp::getUintAcquire(&d_popDisabledGeneration);
766
767 if (disabledGen & 1) {
768 return e_DISABLED; // RETURN
769 }
770
771 Node& node = d_popElement_p[index];
772
773 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
774
775 // If the node is not available for reading:
776 // * if this is a "try" invocation, return
777 // * otherwise, yield and check again, then block
778 // Note that 'e_WRITABLE_AND_BLOCKED != nodeState' since this is the one
779 // consumer.
780
781 if (e_WRITABLE_AND_EMPTY == nodeState) {
782 if (isTry) {
783 return e_EMPTY; // RETURN
784 }
785
787 nodeState = AtomicOp::getUintAcquire(&node.d_state);
788 if (e_WRITABLE_AND_EMPTY == nodeState) {
789 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
790
791 nodeState = AtomicOp::testAndSwapUintAcqRel(
792 &node.d_state,
793 nodeState,
794 e_WRITABLE_AND_BLOCKED);
795
796 while (( e_WRITABLE_AND_EMPTY == nodeState
797 || e_WRITABLE_AND_BLOCKED == nodeState)
798 && disabledGen ==
799 AtomicOp::getUintAcquire(&d_popDisabledGeneration)) {
800 int rv = d_popCondition.wait(&d_popMutex);
801 if (rv) {
802 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
803 e_WRITABLE_AND_BLOCKED,
804 e_WRITABLE_AND_EMPTY);
805 return e_FAILED; // RETURN
806 }
807 nodeState = AtomicOp::getUint(&node.d_state);
808 }
809
810 // The following checks for disablement being the cause of exiting
811 // the 'while' loop.
812
813 if ( e_WRITABLE_AND_EMPTY == nodeState
814 || e_WRITABLE_AND_BLOCKED == nodeState) {
815 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
816 e_WRITABLE_AND_BLOCKED,
817 e_WRITABLE_AND_EMPTY);
818 return e_DISABLED; // RETURN
819 }
820 }
821 }
822
823 SingleProducerSingleConsumerBoundedQueue_PopCompleteGuard<
824 SingleProducerSingleConsumerBoundedQueue<TYPE>, Node>
825 guard(this, &node, index);
826
827#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
828 *value = bslmf::MovableRefUtil::move(node.d_value.object());
829#else
830 *value = node.d_value.object();
831#endif
832
833 return e_SUCCESS;
834}
835
836template <class TYPE>
837int SingleProducerSingleConsumerBoundedQueue<TYPE>::pushBackImp(
838 const TYPE& value,
839 bool isTry)
840{
841 Uint64 index = AtomicOp::getUint64Acquire(&d_pushIndex);
842 const Uint disabledGen =
843 AtomicOp::getUintAcquire(&d_pushDisabledGeneration);
844
845 if (disabledGen & 1) {
846 return e_DISABLED; // RETURN
847 }
848
849 Node& node = d_pushElement_p[index];
850
851 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
852
853 // If the node is not available for writing:
854 // * if this is a "try" invocation, return
855 // * otherwise, yield and check again, then block
856 // Note that 'e_READABLE_AND_BLOCKED != nodeState' since this is the one
857 // producer.
858
859 if (e_READABLE == nodeState) {
860 if (isTry) {
861 return e_FULL; // RETURN
862 }
863
865 nodeState = AtomicOp::getUintAcquire(&node.d_state);
866 if (e_READABLE == nodeState) {
867 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
868
869 nodeState = AtomicOp::testAndSwapUintAcqRel(
870 &node.d_state,
871 e_READABLE,
872 e_READABLE_AND_BLOCKED);
873
874 while (( e_READABLE == nodeState
875 || e_READABLE_AND_BLOCKED == nodeState)
876 && disabledGen ==
877 AtomicOp::getUintAcquire(&d_pushDisabledGeneration)) {
878 int rv = d_pushCondition.wait(&d_pushMutex);
879 if (rv) {
880 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
881 e_READABLE_AND_BLOCKED,
882 e_READABLE);
883 return e_FAILED; // RETURN
884 }
885 nodeState = AtomicOp::getUint(&node.d_state);
886 }
887
888 // The following checks for disablement being the cause of exiting
889 // the 'while' loop.
890
891 if ( e_READABLE == nodeState
892 || e_READABLE_AND_BLOCKED == nodeState) {
893 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
894 e_READABLE_AND_BLOCKED,
895 e_READABLE);
896 return e_DISABLED; // RETURN
897 }
898 }
899 }
900
901 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
902 value,
903 d_allocator_p);
904
905 pushComplete(&node, index);
906
907 return e_SUCCESS;
908}
909
910template <class TYPE>
911int SingleProducerSingleConsumerBoundedQueue<TYPE>::pushBackImp(
913 bool isTry)
914{
915 Uint64 index = AtomicOp::getUint64Acquire(&d_pushIndex);
916 const Uint disabledGen =
917 AtomicOp::getUintAcquire(&d_pushDisabledGeneration);
918
919 if (disabledGen & 1) {
920 return e_DISABLED; // RETURN
921 }
922
923 Node& node = d_pushElement_p[index];
924
925 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
926
927 if (e_READABLE == nodeState) {
928 if (isTry) {
929 return e_FULL; // RETURN
930 }
931
933 nodeState = AtomicOp::getUintAcquire(&node.d_state);
934 if (e_READABLE == nodeState) {
935 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
936
937 nodeState = AtomicOp::testAndSwapUintAcqRel(
938 &node.d_state,
939 e_READABLE,
940 e_READABLE_AND_BLOCKED);
941
942 while (( e_READABLE == nodeState
943 || e_READABLE_AND_BLOCKED == nodeState)
944 && disabledGen ==
945 AtomicOp::getUintAcquire(&d_pushDisabledGeneration)) {
946 int rv = d_pushCondition.wait(&d_pushMutex);
947 if (rv) {
948 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
949 e_READABLE_AND_BLOCKED,
950 e_READABLE);
951 return e_FAILED; // RETURN
952 }
953 nodeState = AtomicOp::getUint(&node.d_state);
954 }
955
956 if ( e_READABLE == nodeState
957 || e_READABLE_AND_BLOCKED == nodeState) {
958 AtomicOp::testAndSwapUintAcqRel(&node.d_state,
959 e_READABLE_AND_BLOCKED,
960 e_READABLE);
961 return e_DISABLED; // RETURN
962 }
963 }
964 }
965
966 TYPE& dummy = value;
967 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
968 dummy,
969 d_allocator_p);
970
971 pushComplete(&node, index);
972
973 return e_SUCCESS;
974}
975
976template <class TYPE>
977inline
978void SingleProducerSingleConsumerBoundedQueue<TYPE>::pushComplete(
979 Node *node,
980 Uint64 index)
981{
982
983 Uint nodeState = AtomicOp::swapUintAcqRel(&node->d_state, e_READABLE);
984 if (e_WRITABLE_AND_BLOCKED == nodeState) {
985 // Queue is no longer empty and the consumer is blocked.
986
987 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
988 {
989 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
990 }
991 d_popCondition.signal();
992 }
993 else if (e_WRITABLE_AND_EMPTY == nodeState) {
994 // Queue is no longer empty.
995
996 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
997 }
998
999 ++index;
1000 if (index == d_pushCapacity) {
1001 index = 0;
1002 }
1003 AtomicOp::setUint64Release(&d_pushIndex, index);
1004}
1005
1006// CREATORS
1007template <class TYPE>
1010 bslma::Allocator *basicAllocator)
1011: d_popElement_p(0)
1012, d_popCapacity(capacity > 0 ? capacity : 1)
1013, d_popPad()
1014, d_pushCapacity(capacity > 0 ? capacity : 1)
1015, d_pushPad()
1016, d_popMutex()
1017, d_popCondition()
1018, d_pushMutex()
1019, d_pushCondition()
1020, d_emptyMutex()
1021, d_emptyCondition()
1022, d_allocator_p(bslma::Default::allocator(basicAllocator))
1023{
1024 AtomicOp::initUint64(&d_popIndex, 0);
1025 AtomicOp::initUint64(&d_pushIndex, 0);
1026
1027 AtomicOp::initUint(&d_popDisabledGeneration, 0);
1028 AtomicOp::initUint(&d_emptyCount, 0);
1029 AtomicOp::initUint(&d_emptyGeneration, 0);
1030 AtomicOp::initUint(&d_pushDisabledGeneration, 0);
1031
1032 d_popElement_p = static_cast<Node *>(
1033 d_allocator_p->allocate(d_popCapacity * sizeof(Node)));
1034
1035 d_pushElement_p = d_popElement_p;
1036
1037 AtomicOp::initUint(&d_popElement_p[0].d_state, e_WRITABLE_AND_EMPTY);
1038 for (bsl::size_t i = 1; i < d_popCapacity; ++i) {
1039 AtomicOp::initUint(&d_popElement_p[i].d_state, e_WRITABLE);
1040 }
1041}
1042
1043template <class TYPE>
1046{
1047 if (d_popElement_p) {
1048 removeAll();
1049 d_allocator_p->deallocate(d_popElement_p);
1050 }
1051}
1052
1053// MANIPULATORS
1054template <class TYPE>
1055inline
1057{
1058 return popFrontImp(value, false);
1059}
1060
1061template <class TYPE>
1062inline
1064{
1065 return pushBackImp(value, false);
1066}
1067
1068template <class TYPE>
1069inline
1075
1076template <class TYPE>
1078{
1079 Uint64 index = AtomicOp::getUint64Acquire(&d_popIndex);
1080 Uint nodeState = AtomicOp::getUintAcquire(
1081 &d_popElement_p[index].d_state);
1082
1083 while (e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState) {
1084 d_popElement_p[index].d_value.object().~TYPE();
1085
1086 AtomicOp::swapUintAcqRel(&d_popElement_p[index].d_state, e_WRITABLE);
1087
1088 ++index;
1089 if (index == d_popCapacity) {
1090 index = 0;
1091 }
1092
1093 nodeState = AtomicOp::getUintAcquire(&d_popElement_p[index].d_state);
1094 }
1095
1096 // If the node subsequent to the last removed element is writable, the
1097 // queue is empty and the node subsequent to the last removed element' must
1098 // be marked as 'e_WRITABLE_AND_EMPTY'.
1099
1100 nodeState = AtomicOp::testAndSwapUintAcqRel(&d_popElement_p[index].d_state,
1101 e_WRITABLE,
1102 e_WRITABLE_AND_EMPTY);
1103
1104 if (e_WRITABLE == nodeState) {
1105 // The queue is empty, increment the empty generation count.
1106
1107 AtomicOp::addUintAcqRel(&d_emptyGeneration, 1);
1108 }
1109 else {
1110 // A 'removeAll' makes the queue empty. Since there has been a
1111 // 'pushBack' before the queue could be marked empty ('e_WRITABLE !=
1112 // nodeState'), increase the empty generation by 2 to note the queue
1113 // was empty at some point during this method call but is no longer
1114 // empty (1 for becoming empty, 1 for leaving the empty state).
1115
1116 AtomicOp::addUintAcqRel(&d_emptyGeneration, 2);
1117 }
1118
1119 AtomicOp::setUint64Release(&d_popIndex, index);
1120
1121 {
1122 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
1123 }
1124 d_pushCondition.signal();
1125
1126 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
1127 {
1128 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1129 }
1130 d_emptyCondition.broadcast();
1131 }
1132}
1133
1134template <class TYPE>
1135inline
1137{
1138 return popFrontImp(value, true);
1139}
1140
1141template <class TYPE>
1142inline
1144 const TYPE& value)
1145{
1146 return pushBackImp(value, true);
1147}
1148
1149template <class TYPE>
1150inline
1156
1157 // Enqueue/Dequeue State
1158
1159template <class TYPE>
1160inline
1162{
1163 incrementUntil(&d_popDisabledGeneration, 1);
1164
1165 {
1166 bslmt::LockGuard<bslmt::Mutex> guard(&d_popMutex);
1167 }
1168 d_popCondition.broadcast();
1169
1170 if (0 < AtomicOp::getUintAcquire(&d_emptyCount)) {
1171 {
1172 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1173 }
1174 d_emptyCondition.broadcast();
1175 }
1176}
1177
1178template <class TYPE>
1179inline
1181{
1182 incrementUntil(&d_pushDisabledGeneration, 1);
1183
1184 {
1185 bslmt::LockGuard<bslmt::Mutex> guard(&d_pushMutex);
1186 }
1187 d_pushCondition.broadcast();
1188}
1189
1190template <class TYPE>
1191inline
1193{
1194 incrementUntil(&d_popDisabledGeneration, 0);
1195}
1196
1197template <class TYPE>
1198inline
1200{
1201 incrementUntil(&d_pushDisabledGeneration, 0);
1202}
1203
1204// ACCESSORS
1205template <class TYPE>
1206inline
1208{
1209 return d_popCapacity;
1210}
1211
1212template <class TYPE>
1213inline
1215{
1216 return 0 == (AtomicOp::getUintAcquire(&d_emptyGeneration) & 1);
1217}
1218
1219template <class TYPE>
1220inline
1222{
1223 Node& node = d_pushElement_p[AtomicOp::getUint64Acquire(
1224 &d_pushIndex)];
1225 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
1226
1227 return e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState;
1228}
1229
1230template <class TYPE>
1231inline
1233{
1234 return 1 == (AtomicOp::getUintAcquire(&d_popDisabledGeneration) & 1);
1235}
1236
1237template <class TYPE>
1238inline
1240{
1241 return 1 == (AtomicOp::getUintAcquire(&d_pushDisabledGeneration) & 1);
1242}
1243
1244template <class TYPE>
1245inline
1247{
1248 Uint64 popIndex = AtomicOp::getUint64Acquire(&d_popIndex);
1249 Uint64 pushIndex = AtomicOp::getUint64Acquire(&d_pushIndex);
1250 Node& node = d_pushElement_p[pushIndex];
1251 Uint nodeState = AtomicOp::getUintAcquire(&node.d_state);
1252
1253 if (e_READABLE == nodeState || e_READABLE_AND_BLOCKED == nodeState) {
1254 return d_popCapacity; // RETURN
1255 }
1256
1257 return static_cast<bsl::size_t>( pushIndex >= popIndex
1258 ? pushIndex - popIndex
1259 : pushIndex + d_popCapacity - popIndex);
1260}
1261
1262template <class TYPE>
1264{
1265 AtomicOp::addUintAcqRel(&d_emptyCount, 1);
1266
1267 const Uint initEmptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1268
1269 const Uint disabledGen =
1270 AtomicOp::getUintAcquire(&d_popDisabledGeneration);
1271
1272 if (disabledGen & 1) {
1273 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1274 return e_DISABLED; // RETURN
1275 }
1276
1277 if (0 == (initEmptyGen & 1)) {
1278 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1279 return e_SUCCESS; // RETURN
1280 }
1281
1282 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1283
1284 Uint emptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1285
1286 while ( initEmptyGen == emptyGen
1287 && disabledGen ==
1288 AtomicOp::getUintAcquire(&d_popDisabledGeneration)) {
1289 int rv = d_emptyCondition.wait(&d_emptyMutex);
1290 if (rv) {
1291 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1292 return e_FAILED; // RETURN
1293 }
1294 emptyGen = AtomicOp::getUintAcquire(&d_emptyGeneration);
1295 }
1296
1297 AtomicOp::addUintAcqRel(&d_emptyCount, -1);
1298
1299 if (initEmptyGen == emptyGen) {
1300 return e_DISABLED; // RETURN
1301 }
1302
1303 return e_SUCCESS;
1304}
1305
1306 // Aspects
1307
1308template <class TYPE>
1309inline
1311 const
1312{
1313 return d_allocator_p;
1314}
1315
1316} // close package namespace
1317
1318
1319#endif
1320
1321// ----------------------------------------------------------------------------
1322// Copyright 2019 Bloomberg Finance L.P.
1323//
1324// Licensed under the Apache License, Version 2.0 (the "License");
1325// you may not use this file except in compliance with the License.
1326// You may obtain a copy of the License at
1327//
1328// http://www.apache.org/licenses/LICENSE-2.0
1329//
1330// Unless required by applicable law or agreed to in writing, software
1331// distributed under the License is distributed on an "AS IS" BASIS,
1332// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1333// See the License for the specific language governing permissions and
1334// limitations under the License.
1335// ----------------------------- END-OF-FILE ----------------------------------
1336
1337/** @} */
1338/** @} */
1339/** @} */
#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:692
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:297
void removeAll()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1077
int waitUntilEmpty() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1263
bool isPushBackDisabled() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1239
bsl::size_t capacity() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1207
void disablePopFront()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1161
int tryPushBack(const TYPE &value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1143
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1310
int pushBack(const TYPE &value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1063
bool isPopFrontDisabled() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1232
TYPE value_type
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:508
bool isEmpty() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1214
~SingleProducerSingleConsumerBoundedQueue()
Destroy this object.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1045
void enablePushBack()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1199
bool isFull() const
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1221
void disablePushBack()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1180
bsl::size_t numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1246
int tryPopFront(TYPE *value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1136
int popFront(TYPE *value)
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1056
void enablePopFront()
Definition bdlcc_singleproducersingleconsumerboundedqueue.h:1192
Definition bslma_allocator.h:457
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:751
Definition bslmt_condition.h:220
Definition bslmt_lockguard.h:234
Definition bslmt_mutex.h:315
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlcc_boundedqueue.h:270
Definition balxml_encoderoptions.h:68
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1642
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1599
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1060
@ e_CACHE_LINE_SIZE
Definition bslmt_platform.h:213
static void yield()
Definition bslmt_threadutil.h:1013
Definition bsls_atomicoperations.h:834
static void initUint64(AtomicTypes::Uint64 *atomicUint, Types::Uint64 initialValue=0)
Definition bsls_atomicoperations.h:2121
static void initUint(AtomicTypes::Uint *atomicUint, unsigned int initialValue=0)
Definition bsls_atomicoperations.h:1922
unsigned long long Uint64
Definition bsls_types.h:137
Definition bsls_objectbuffer.h:276