BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_boundedqueue.h
Go to the documentation of this file.
1/// @file bdlcc_boundedqueue.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_boundedqueue.h -*-C++-*-
8
9#ifndef INCLUDED_BDLCC_BOUNDEDQUEUE
10#define INCLUDED_BDLCC_BOUNDEDQUEUE
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bdlcc_boundedqueue bdlcc_boundedqueue
16/// @brief Provide a thread-aware bounded queue of values.
17/// @addtogroup bdl
18/// @{
19/// @addtogroup bdlcc
20/// @{
21/// @addtogroup bdlcc_boundedqueue
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bdlcc_boundedqueue-purpose"> Purpose</a>
26/// * <a href="#bdlcc_boundedqueue-classes"> Classes </a>
27/// * <a href="#bdlcc_boundedqueue-description"> Description </a>
28/// * <a href="#bdlcc_boundedqueue-comparison-to-fixedqueue"> Comparison To FixedQueue </a>
29/// * <a href="#bdlcc_boundedqueue-template-requirements"> Template Requirements </a>
30/// * <a href="#bdlcc_boundedqueue-exception-safety"> Exception Safety </a>
31/// * <a href="#bdlcc_boundedqueue-move-semantics-in-c-03"> Move Semantics in C++03 </a>
32/// * <a href="#bdlcc_boundedqueue-usage"> Usage </a>
33/// * <a href="#bdlcc_boundedqueue-example-1-a-simple-thread-pool"> Example 1: A Simple Thread Pool </a>
34///
35/// # Purpose {#bdlcc_boundedqueue-purpose}
36/// Provide a thread-aware bounded queue of values.
37///
38/// # Classes {#bdlcc_boundedqueue-classes}
39///
40/// - bdlcc::BoundedQueue: thread-aware bounded queue of `TYPE`
41///
42/// @see bdlcc_fixedqueue
43///
44/// # Description {#bdlcc_boundedqueue-description}
45/// This component defines a type, `bdlcc::BoundedQueue`, that
46/// provides an efficient, thread-aware bounded (capacity fixed at construction)
47/// queue of values. This class is ideal for synchronization and communication
48/// between threads in a producer-consumer model when a bounded queue is
49/// appropriate. Under most cicrumstances developers should prefer
50/// this component to the older {bdlcc_fixedqueue} (see {Comparison to
51/// FixedQueue}).
52///
53/// The queue provides `pushBack` and `popFront` methods for pushing data into
54/// the queue and popping data from the queue. When the queue is full, the
55/// `pushBack` methods block until data is removed from the queue. When the
56/// queue is empty, the `popFront` methods block until data appears in the
57/// queue. Non-blocking methods `tryPushBack` and `tryPopFront` are also
58/// provided. The `tryPushBack` method fails immediately, returning a non-zero
59/// value, if the queue is full. The `tryPopFront` method fails immediately,
60/// returning a non-zero value, if the queue is empty.
61///
62/// The queue may be placed into a "enqueue disabled" state using the
63/// `disablePushBack` method. When disabled, `pushBack` and `tryPushBack` fail
64/// immediately and return an error code. Any threads blocked in `pushBack`
65/// when the queue is enqueue disabled return from `pushBack` immediately and
66/// return an error code. The queue may be restored to normal operation with
67/// the `enablePushBack` method.
68///
69/// The queue may be placed into a "dequeue disabled" state using the
70/// `disablePopFront` method. When dequeue disabled, `popFront` and
71/// `tryPopFront` fail immediately and return an error code. Any threads
72/// blocked in `popFront` when the queue is dequeue disabled return from
73/// `popFront` immediately and return an error code. The queue may be restored
74/// to normal operation with the `enablePopFront` method.
75///
76/// ## Comparison To FixedQueue {#bdlcc_boundedqueue-comparison-to-fixedqueue}
77///
78///
79/// Both `bdlcc::FixedQueue` and `bdlcc::BoundedQueue` provide thread-aware
80/// bounded queues. Under most circumstances developers should prefer
81/// {bdlcc_boundedqueue}: it is newer, has additional features, and provides
82/// better performance under most circumstances. `bdlcc::BoundedQueue` is not
83/// quite a drop in replacement for `bdlcc::FixedQueue` so both types are
84/// currently maintained. There is additional information about
85/// performance of various queues in the article Concurrent Queue Evaluation
86/// (https://tinyurl.com/mr2un9f7).
87///
88/// ## Template Requirements {#bdlcc_boundedqueue-template-requirements}
89///
90///
91/// `bdlcc::BoundedQueue` is a template that is parameterized on the type of
92/// element contained within the queue. The supplied template argument, `TYPE`,
93/// must provide both a default constructor and a copy constructor, as well as
94/// an assignment operator. If the default constructor accepts a
95/// `bslma::Allocator *`, `TYPE` must declare the uses `bslma::Allocator` trait
96/// (see @ref bslma_usesbslmaallocator ) so that the allocator of the queue is
97/// propagated to the elements contained in the queue.
98///
99/// ## Exception Safety {#bdlcc_boundedqueue-exception-safety}
100///
101///
102/// A `bdlcc::BoundedQueue` is exception neutral, and all of the methods of
103/// `bdlcc::BoundedQueue` provide the basic exception safety guarantee (see
104/// @ref bsldoc_glossary ). If an exception occurs while writing to an element, the
105/// element is marked unusable until after a read attempt from the element (at
106/// which point the element is "reclaimed"). This failure to write does not
107/// increment the result returned by `numElements`. Hence,
108/// `numElements() == capacity()` is not a valid replacement for `isFull()`.
109///
110/// ## Move Semantics in C++03 {#bdlcc_boundedqueue-move-semantics-in-c-03}
111///
112///
113/// Move-only types are supported by `bdlcc::BoundedQueue` on C++11 platforms
114/// only (where `BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES` is defined), and are
115/// not supported on C++03 platforms. Unfortunately, in C++03, there are user
116/// types where a `bslmf::MovableRef` will not safely degrade to a lvalue
117/// reference when a move constructor is not available (types providing a
118/// constructor template taking any type), so `bslmf::MovableRefUtil::move`
119/// cannot be used directly on a user supplied template type. See internal bug
120/// report 99039150 for more information.
121///
122/// ## Usage {#bdlcc_boundedqueue-usage}
123///
124///
125/// This section illustrates intended use of this component.
126///
127/// ### Example 1: A Simple Thread Pool {#bdlcc_boundedqueue-example-1-a-simple-thread-pool}
128///
129///
130/// In the following example a `bdlcc::BoundedQueue` is used to communicate
131/// between a single "producer" thread and multiple "consumer" threads. The
132/// "producer" will push work requests onto the queue, and each "consumer" will
133/// iteratively take a work request from the queue and service the request.
134/// This example shows a partial, simplified implementation of the
135/// `bdlmt::FixedThreadPool` class. See component @ref bdlmt_fixedthreadpool for
136/// more information.
137///
138/// First, we define a utility classes that handles a simple "work item":
139/// @code
140/// /// Work data...
141/// struct my_WorkData {
142/// };
143///
144/// struct my_WorkRequest {
145/// enum RequestType {
146/// e_WORK = 1,
147/// e_STOP = 2
148/// };
149///
150/// RequestType d_type;
151/// my_WorkData d_data;
152/// // Work data...
153/// };
154/// @endcode
155/// Next, we provide a simple function to service an individual work item. The
156/// details are unimportant for this example:
157/// @code
158/// /// Do some work based upon the specified `data`.
159/// void myDoWork(const my_WorkData& data)
160/// {
161/// // do some stuff...
162/// (void)data;
163/// }
164/// @endcode
165/// Then, we define a `myConsumer` function that will pop elements off the queue
166/// and process them. Note that the call to `queue->popFront()` will block
167/// until there is an element available on the queue. This function will be
168/// executed in multiple threads, so that each thread waits in
169/// `queue->popFront()`, and `bdlcc::BoundedQueue` guarantees that each thread
170/// gets a unique element from the queue:
171/// @code
172/// /// Pop elements from the specified `queue`.
173/// void myConsumer(bdlcc::BoundedQueue<my_WorkRequest> *queue)
174/// {
175/// while (1) {
176/// // `popFront()` will wait for a `my_WorkRequest` until available.
177///
178/// my_WorkRequest item;
179/// item.d_type = my_WorkRequest::e_WORK;
180///
181/// assert(0 == queue->popFront(&item));
182///
183/// if (item.d_type == my_WorkRequest::e_STOP) { break; }
184/// myDoWork(item.d_data);
185/// }
186/// }
187/// @endcode
188/// Finally, we define a `myProducer` function that serves multiple roles: it
189/// creates the `bdlcc::BoundedQueue`, starts the consumer threads, and then
190/// produces and enqueues work items. When work requests are exhausted, this
191/// function enqueues one `e_STOP` item for each consumer queue. This `e_STOP`
192/// item indicates to the consumer thread to terminate its thread-handling
193/// function.
194///
195/// Note that, although the producer cannot control which thread `pop`s a
196/// particular work item, it can rely on the knowledge that each consumer thread
197/// will read a single `e_STOP` item and then terminate.
198/// @code
199/// /// Create a queue, start the specified `numThreads` consumer threads,
200/// /// produce and enqueue work.
201/// void myProducer(int numThreads)
202/// {
203/// enum {
204/// k_MAX_QUEUE_LENGTH = 100,
205/// k_NUM_WORK_ITEMS = 1000
206/// };
207///
208/// bdlcc::BoundedQueue<my_WorkRequest> queue(k_MAX_QUEUE_LENGTH);
209///
210/// bslmt::ThreadGroup consumerThreads;
211/// consumerThreads.addThreads(bdlf::BindUtil::bind(&myConsumer, &queue),
212/// numThreads);
213///
214/// for (int i = 0; i < k_NUM_WORK_ITEMS; ++i) {
215/// my_WorkRequest item;
216/// item.d_type = my_WorkRequest::e_WORK;
217/// item.d_data = my_WorkData(); // some stuff to do
218/// queue.pushBack(item);
219/// }
220///
221/// for (int i = 0; i < numThreads; ++i) {
222/// my_WorkRequest item;
223/// item.d_type = my_WorkRequest::e_STOP;
224/// queue.pushBack(item);
225/// }
226///
227/// consumerThreads.joinAll();
228/// }
229/// @endcode
230/// @}
231/** @} */
232/** @} */
233
234/** @addtogroup bdl
235 * @{
236 */
237/** @addtogroup bdlcc
238 * @{
239 */
240/** @addtogroup bdlcc_boundedqueue
241 * @{
242 */
243
244#include <bdlscm_version.h>
245
246#include <bdlb_bitutil.h>
247
249
251
253#include <bslmf_movableref.h>
255
256#include <bslmt_condition.h>
258#include <bslmt_lockguard.h>
259#include <bslmt_mutex.h>
260
261#include <bsls_assert.h>
263#include <bsls_objectbuffer.h>
264#include <bsls_types.h>
265
266#include <bsl_climits.h>
267#include <bsl_cstdint.h>
268
269
270namespace bdlcc {
271
272 // ===================================
273 // class BoundedQueue_PopCompleteGuard
274 // ===================================
275
276/// This class implements a guard that invokes `TYPE::popComplete` on a `NODE`
277/// upon destruction.
278///
279/// See @ref bdlcc_boundedqueue
280template <class TYPE, class NODE>
282
283 // DATA
284 TYPE *d_queue_p; // managed queue owning the managed node
285 NODE *d_node_p; // managed node
286
287 private:
288 // NOT IMPLEMENTED
293
294 public:
295 // CREATORS
296
297 /// Create a `popComplete` guard managing the specified `queue` and `node`.
298 BoundedQueue_PopCompleteGuard(TYPE *queue, NODE *node);
299
300 /// Destroy this object and invoke the `TYPE::popComplete` method with the
301 /// managed `node`.
303};
304
305 // ===============================================
306 // class BoundedQueue_PushExceptionCompleteProctor
307 // ===============================================
308
309/// This class implements a proctor that invokes `TYPE::pushExceptionComplete`
310/// upon destruction unless `release` has been called.
311///
312/// See @ref bdlcc_boundedqueue
313template <class TYPE>
315
316 // DATA
317 TYPE *d_queue_p; // managed queue
318
319 private:
320 // NOT IMPLEMENTED
326
327 public:
328 // CREATORS
329
330 /// Create a `pushExceptionComplete` proctor that conditionally manages the
331 /// specified `queue` (if non-zero).
332 explicit
334
335 /// Destroy this object and, if `release` has not been invoked', invoke the
336 /// managed queue's `pushExceptionComplete` method.
338
339 // MANIPULATORS
340
341 /// Release from management the queue currently managed by this proctor.
342 /// If no queue is currently managed, this method has no effect.
343 void release();
344};
345
346 // ========================
347 // struct BoundedQueue_Node
348 // ========================
349
350/// This class implements the queue's node. A node stores an instance of the
351/// specified (template parameter) `TYPE`, and provides an accessor
352/// `isUnconstructed` that indicates whether the value of the node was
353/// correctly constructed. If `isUnconstructed` is `false`, then the value
354/// (`d_value`) refers to a valid object. If `isUnconstructed` is `true` then
355/// `d_value` does not refer to a valid object, it does not represent a value
356/// in this queue, and the destructor of `d_value` should not be called. The
357/// specified (template parameter) type `RECLAIMABLE` is used to provide a
358/// compile time optimization for the footprint of this template when the value
359/// of `isUnconstructed` is known at compile-time. If `RECLAIMABLE` is `false`
360/// then it can be determined at compile time that the construction of `TYPE`
361/// will uncoditionally succeed (e.g., it `IsBitwiseCopyable`), and the
362/// `isUnconstructed` property does not require a data member to be accessed at
363/// run-time.
364template <class TYPE, bool RECLAIMABLE>
366
367template <class TYPE>
368struct BoundedQueue_Node<TYPE, true> {
369 private:
370 // DATA
371 bool d_isUnconstructedFlag; // node suffered exception
372
373 public:
374 // PUBLIC DATA
376
377 // MANIPULATORS
378
379 /// If the specified `isUnconstructedFlag` is `false`, then `d_value`
380 /// refers to a valid object of (the template parameter) `TYPE`, otherwise
381 /// (if `isUnconstrucedFlag` is `true`) `d_value` does not refer to a valid
382 /// object (because the attempt to construct `TYPE` resulted in an expection).
383 ///
384 /// \note Note that this method is normally invoked after each write
385 /// to `d_value`.
386 void setIsUnconstructed(bool isUnconstructedFlag);
387
388 // ACCESSORS
389
390 /// Return whether an exception occurred when last writing to `d_value`.
391 bool isUnconstructed() const;
392};
393
394template <class TYPE>
395struct BoundedQueue_Node<TYPE, false> {
396 // PUBLIC DATA
398
399 // MANIPULATORS
400
401 /// Do nothing.
402 void setIsUnconstructed(bool /* value */);
403
404 // ACCESSORS
405
406 /// Return `false`.
407 bool isUnconstructed() const;
408};
409
410 // ==================
411 // class BoundedQueue
412 // ==================
413
414/// This class provides a thread-safe bounded queue of values.
415///
416/// See @ref bdlcc_boundedqueue
417template <class TYPE>
419
420 // PRIVATE CONSTANTS
421
422 // The following constants are used to maintain the queue's `d_popCount`
423 // and `d_pushCount` values. See *Implementation* *Note* for details.
424
425 static const bsls::Types::Uint64 k_STARTED_MASK = 0x00000000ffffffffLL;
426 static const bsls::Types::Uint64 k_STARTED_INC = 0x0000000000000001LL;
427 static const bsls::Types::Uint64 k_STARTED_DEC = 0xffffffffffffffffLL;
428 static const bsls::Types::Uint64 k_FINISHED_INC = 0x0000000100000000LL;
429 static const unsigned int k_FINISHED_SHIFT = 32;
430
431 static const unsigned int k_MAXIMUM_CIRCULAR_DIFFERENCE =
432 static_cast<unsigned int>(1) << (sizeof(unsigned int) * CHAR_BIT - 1);
433
434 // PRIVATE TYPES
435 typedef unsigned int Uint;
436 typedef typename bsls::Types::Uint64 Uint64;
437 typedef typename bsls::AtomicOperations::AtomicTypes::Uint AtomicUint;
438 typedef typename bsls::AtomicOperations::AtomicTypes::Uint64 AtomicUint64;
439
440 typedef typename bsls::AtomicOperations AtomicOp;
441
442 typedef BoundedQueue_Node<TYPE,
444
445 // DATA
446 bslmt::FastPostSemaphore d_pushSemaphore; // synchronization primitive
447 // restricting access to
448 // empty elements and
449 // providing
450 // enablement/disablement of
451 // "push" operations
452
453 AtomicUint64 d_pushCount; // count of "push"
454 // operations started and
455 // completed, used to detect
456 // a quiescent state for
457 // safely incrementing
458 // number of available
459 // elements
460
461 AtomicUint64 d_pushIndex; // index of next enqueue
462 // element location
463
464 bslmt::FastPostSemaphore d_popSemaphore; // synchronization primitive
465 // restricting access to
466 // available elements and
467 // providing
468 // enablement/disablement of
469 // "pop" operations
470
471 AtomicUint64 d_popCount; // count of "pop" operations
472 // started and completed,
473 // used to detect a
474 // quiescent state for
475 // safely incrementing
476 // number of empty elements
477
478 AtomicUint64 d_popIndex; // index of next dequeue
479 // element location
480
481 mutable AtomicUint d_emptyWaiterCount; // circular count of
482 // `waitUntilEmpty`
483 // invocations
484
485 AtomicUint d_emptyCountSeen; // maximum
486 // `d_emptyWaiterCount` seen
487 // prior to the queue being
488 // observed as empty; used
489 // to detect most very short
490 // lived transitions to the
491 // queue being empty
492
493 mutable bslmt::Mutex d_emptyMutex; // blocking point for
494 // `waitUntilEmpty`
495
496 mutable bslmt::Condition d_emptyCondition; // condition variable for
497 // `waitUntilEmpty`
498
499 Node *d_element_p; // array of elements that
500 // comprise the bounded
501 // queue
502
503 const Uint64 d_capacity; // capacity of the queue
504
505 bslma::Allocator *d_allocator_p; // allocator, held not owned
506
507 // FRIENDS
509 BoundedQueue<TYPE>,
510 typename BoundedQueue<TYPE>::Node>;
511
513 BoundedQueue<TYPE> >;
514
515 // PRIVATE CLASS METHODS
516
517 /// Return `true` if the specified `lhs` is circularly greater than the
518 /// specified `rhs`, and `false` otherwise. `lhs` is cicularly greater
519 /// than `rhs` if `lhs` is equal to a value obtained by adding a value
520 /// in `[1 .. 2^31]` to `rhs`.
521 static bool circularlyGreater(Uint lhs, Uint rhs);
522
523 /// Return `true` if the specified `count` implies a quiescent state (see
524 /// **Implementation Note**), and `false` otherwise. A quiescent state
525 /// indicates there is a (possibly zero length) contiguous set of elements
526 /// that can safely be made available to the operation complementary to the
527 /// operation `count` tracks (pop and push are complementary operations).
528 static bool isQuiescentState(bsls::Types::Uint64 count);
529
530 /// Update the specified `count` to indicate that an operation (or the
531 /// optionally specified `num` operations) has completed, and return the
532 /// updated `count` value. Marking an operation finished means that
533 /// `isQuiscentState` **may** now be true.
534 ///
535 /// \pre The behavior is undefined unless `markStartedOperation` was previously called on this `count`,
536 /// and a corresponding `markFinishedOperation` or `unmarkStartOperation` has not already been called.
537 ///
538 /// \note Note that the "operation" that has
539 /// finished refers to either a push or pop (depending on whether this is
540 /// applied to `d_pushCount` or `d_popCount`).
541 static Uint64 markFinishedOperation(AtomicUint64 *count);
542 static Uint64 markFinishedOperation(AtomicUint64 *count, int num);
543
544 /// Update the specified `count` to indicate that a node that suffered an
545 /// exception has been reclaimed. Marking a node reclaimed can *not* alter
546 /// the value of `isQuiescentState`, but does increase the size of the
547 /// contiguous set of elements that will eventially be made available to
548 /// the operation complementary to the operation `count` tracks (pop and push are complementary operations).
549 ///
550 /// \note Note that this method does not
551 /// require a previous call to `markStartedOperation` and does not meet the
552 /// requirements for `markFinishedOperation` or `unmarkStartOperation` to
553 /// be invoked.
554 static void markReclaimed(AtomicUint64 *count);
555
556 /// Update the specified `count` to indicate that an operation (or the
557 /// optionally specified `num` operations) has started, and return the
558 /// updated `count` value. Marking an operation started means that
559 /// `isQuiescentState` is not true and will not be true until
560 /// `markFinishedOperation` or `unmarkStartedOperation` is invoked.
561 ///
562 /// \note Note that the "operation" that has started refers to either a push or pop
563 /// (depending on whether this is applied to `d_pushCount` or
564 /// `d_popCount`).
565 static void markStartedOperation(AtomicUint64 *count);
566 static void markStartedOperation(AtomicUint64 *count, int num);
567
568 /// Update the specified `count` to indicate that an operation has aborted
569 /// without finishing, and return the updated `count` value. Marking a
570 /// started operation as having aborted means that `isQuiscentState` *may* now be true.
571 ///
572 /// \pre The behavior is undefined unless `markStartedOperation`
573 /// was previously called on this `count`, and a corresponding
574 /// `markFinishedOperation` or `unmarkStartOperation` has not already been called.
575 ///
576 /// \note Note that the "operation" that has aborted refers to either a
577 /// push or pop (depending on whether this is applied to `d_pushCount` or
578 /// `d_popCount`).
579 static Uint64 unmarkStartedOperation(AtomicUint64 *count);
580
581 // PRIVATE MANIPULATORS
582
583 /// Destruct the value stored in the specified `node`, and mark the `node`
584 /// writable. This method is used within `popFrontHelper` by a guard to
585 /// complete the reclamation of a node in the presence of an exception.
586 void popComplete(Node *node);
587
588 /// Remove the element from the front of this queue and load that element
589 /// into the specified `value`. This method is invoked by `popFront` and
590 /// `tryPopFront` once an element is available.
591 void popFrontHelper(TYPE *value);
592
593 /// Mark a "push" operation as complete, and `post` to the `d_popSemaphore`
594 /// if appropriate.
595 void pushComplete();
596
597 /// Remove the indicator for a started push operation, and `post` to the
598 /// `d_popSemaphore` if appropriate. This method is used within
599 /// `pushFront` by a proctor to complete the marking of a node to reclaim
600 /// in the presence of an exception.
601 void pushExceptionComplete();
602
603 /// If the specified `emptyCount` is (circularly) greater than
604 /// `d_emptyCountSeen`, assign `d_emptyCountSeen` the value of `emptyCount`
605 /// and return `true`. Otherwise, return `false`. A return value of
606 /// `true` indicates this thread *must* signal any waiting threads.
607 ///
608 /// \note Note that a return value of `false` indicates another thread has (or will)
609 /// signal the queue is empty and this thread does not need to signal.
610 bool updateEmptyCountSeen(Uint emptyCount);
611
612 private:
613 // NOT IMPLEMENTED
614 BoundedQueue(const BoundedQueue&);
615 BoundedQueue& operator=(const BoundedQueue&);
616
617 public:
618 // TRAITS
619 BSLMF_NESTED_TRAIT_DECLARATION(BoundedQueue, bslma::UsesBslmaAllocator);
620
621 // PUBLIC TYPES
622 typedef TYPE value_type; // The type for elements.
623
624 // PUBLIC CONSTANTS
625 enum {
626 e_SUCCESS = 0, // must be 0
627 e_EMPTY = -1,
628 e_FULL = -2,
629 e_DISABLED = -3,
630 e_FAILED = -4
631 };
632
633 // CREATORS
634
635 /// Create a thread-aware queue with at least the specified `capacity`.
636 /// Optionally specify a `basicAllocator` used to supply memory. If
637 /// `basicAllocator` is 0, the currently installed default allocator is
638 /// used.
639 explicit
640 BoundedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
641
642 /// Destroy this object.
644
645 // MANIPULATORS
646
647 /// Remove the element from the front of this queue and load that
648 /// element into the specified `value`. If the queue is empty, block
649 /// until it is not empty. Return 0 on success, and a non-zero value
650 /// otherwise. Specifically, return `e_SUCCESS` on success,
651 /// `e_DISABLED` if `isPopFrontDisabled()` and `e_FAILED` if an error
652 /// occurs. On failure, `value` is not changed. Threads blocked due to
653 /// the queue being empty will return `e_DISABLED` if `disablePopFront`
654 /// is invoked.
655 int popFront(TYPE *value);
656
657 /// Append the specified `value` to the back of this queue. If the
658 /// queue is full, block until it is not full. Return 0 on success, and
659 /// a non-zero value otherwise. Specifically, return `e_SUCCESS` on
660 /// success, `e_DISABLED` if `isPushBackDisabled()` and `e_FAILED` if an
661 /// error occurs. Threads blocked due to the queue being full will
662 /// return `e_DISABLED` if `disablePushBack` is invoked.
663 int pushBack(const TYPE& value);
664
665 /// Append the specified move-insertable `value` to the back of this
666 /// queue. If the queue is full, block until it is not full. `value`
667 /// is left in a valid but unspecified state. Return 0 on success, and
668 /// a non-zero value otherwise. Specifically, return `e_SUCCESS` on
669 /// success, `e_DISABLED` if `isPushBackDisabled()` and `e_FAILED` if an
670 /// error occurs. On failure, `value` is not changed. Threads blocked
671 /// due to the queue being full will return `e_DISABLED` if
672 /// `disablePushBack` is invoked.
674
675 /// Remove all items currently in this queue.
676 /// \note Note that this operation
677 /// is not atomic; if other threads are concurrently pushing items into
678 /// the queue the result of `numElements()` after this function returns
679 /// is not guaranteed to be 0.
680 void removeAll();
681
682 /// Attempt to remove the element from the front of this queue without
683 /// blocking, and, if successful, load the specified `value` with the
684 /// removed element. Return 0 on success, and a non-zero value
685 /// otherwise. Specifically, return `e_SUCCESS` on success,
686 /// `e_DISABLED` if `isPopFrontDisabled()`, `e_EMPTY` if
687 /// `!isPopFrontDisabled()` and the queue was empty, and `e_FAILED` if
688 /// an error occurs. On failure, `value` is not changed.
689 int tryPopFront(TYPE *value);
690
691 /// Append the specified `value` to the back of this queue. Return 0 on
692 /// success, and a non-zero value otherwise. Specifically, return
693 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`,
694 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full, and
695 /// `e_FAILED` if an error occurs.
696 int tryPushBack(const TYPE& value);
697
698 /// Append the specified move-insertable `value` to the back of this
699 /// queue. `value` is left in a valid but unspecified state. Return 0
700 /// on success, and a non-zero value otherwise. Specifically, return
701 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`,
702 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full, and
703 /// `e_FAILED` if an error occurs. On failure, `value` is not changed.
705
706 // Enqueue/Dequeue State
707
708 /// Disable dequeueing from this queue. All subsequent invocations of
709 /// `popFront` or `tryPopFront` will fail immediately. All blocked
710 /// invocations of `popFront` and `waitUntilEmpty` will fail
711 /// immediately. If the queue is already dequeue disabled, this method
712 /// has no effect.
714
715 /// Disable enqueueing into this queue. All subsequent invocations of
716 /// `pushBack` or `tryPushBack` will fail immediately. All blocked
717 /// invocations of `pushBack` will fail immediately. If the queue is
718 /// already enqueue disabled, this method has no effect.
720
721 /// Enable dequeueing. If the queue is not dequeue disabled, this call
722 /// has no effect.
724
725 /// Enable queuing. If the queue is not enqueue disabled, this call has
726 /// no effect.
728
729 // ACCESSORS
730
731 /// Return the maximum number of elements that may be stored in this queue.
732 ///
733 /// \note Note that the value returned may be greater than that supplied at
734 /// construction.
735 bsl::size_t capacity() const;
736
737 /// Return `true` if this queue is empty (has no elements), or `false`
738 /// otherwise.
739 bool isEmpty() const;
740
741 /// Return `true` if this queue is full (has no available capacity), or `false` otherwise.
742 ///
743 /// \note Note that for unbounded queues, this method always
744 /// returns `false`.
745 bool isFull() const;
746
747 /// Return `true` if this queue is dequeue disabled, and `false` otherwise.
748 ///
749 /// \note Note that the queue is created in the "dequeue enabled" state.
750 bool isPopFrontDisabled() const;
751
752 /// Return `true` if this queue is enqueue disabled, and `false` otherwise.
753 ///
754 /// \note Note that the queue is created in the "enqueue enabled" state.
755 bool isPushBackDisabled() const;
756
757 /// Returns the number of elements currently in this queue.
758 ///
759 /// \note Note that `numElements() == capacity()` is not a valid replacement for `isFull`
760 /// (see {Exception Safety} for details).
761 bsl::size_t numElements() const;
762
763 /// Block until all the elements in this queue are removed. Return 0 on
764 /// success, and a non-zero value otherwise. Specifically, return
765 /// `e_SUCCESS` on success, `e_DISABLED` if `!isEmpty() &&
766 /// isPopFrontDisabled()`. A blocked thread waiting for the queue to empty
767 /// will return `e_DISABLED` if `disablePopFront` is invoked.
768 int waitUntilEmpty() const;
769
770 // Aspects
771
772 /// Return the allocator used by this object to supply memory.
774};
775
776// ============================================================================
777// INLINE DEFINITIONS
778// ============================================================================
779
780 // -----------------------------------
781 // class BoundedQueue_PopCompleteGuard
782 // -----------------------------------
783
784// CREATORS
785template <class TYPE, class NODE>
786inline
788 BoundedQueue_PopCompleteGuard(TYPE *queue, NODE *node)
789: d_queue_p(queue)
790, d_node_p(node)
791{
792}
793
794template <class TYPE, class NODE>
795inline
800
801 // -----------------------------------------------
802 // class BoundedQueue_PushExceptionCompleteProctor
803 // -----------------------------------------------
804
805// CREATORS
806template <class TYPE>
807inline
813
814template <class TYPE>
815inline
818{
819 if (d_queue_p) {
820 d_queue_p->pushExceptionComplete();
821 }
822}
823
824// MANIPULATORS
825template <class TYPE>
826inline
831
832 // ------------------------
833 // struct BoundedQueue_Node
834 // ------------------------
835
836// MANIPULATORS
837template <class TYPE>
838inline
840 bool isUnconstructedFlag)
841{
842 d_isUnconstructedFlag = isUnconstructedFlag;
843}
844
845template <class TYPE>
846inline
848 bool /* isUnconstrutedFlag */)
849{
850}
851
852// ACCESSORS
853template <class TYPE>
854inline
856{
857 return d_isUnconstructedFlag;
858}
859
860template <class TYPE>
861inline
863{
864 return false;
865}
866
867 // ------------------
868 // class BoundedQueue
869 // ------------------
870
871// PRIVATE CLASS METHODS
872template <class TYPE>
873inline
874bool BoundedQueue<TYPE>::circularlyGreater(Uint lhs, Uint rhs)
875{
876 return lhs > rhs ? (lhs - rhs) <= k_MAXIMUM_CIRCULAR_DIFFERENCE
877 : (rhs - lhs) > k_MAXIMUM_CIRCULAR_DIFFERENCE;
878}
879
880template <class TYPE>
881inline
882bool BoundedQueue<TYPE>::isQuiescentState(bsls::Types::Uint64 count)
883{
884 return (count >> k_FINISHED_SHIFT) == (count & k_STARTED_MASK);
885}
886
887template <class TYPE>
888inline
889bsls::Types::Uint64 BoundedQueue<TYPE>::markFinishedOperation(
890 AtomicUint64 *count)
891{
892 return AtomicOp::addUint64NvAcqRel(count, k_FINISHED_INC);
893}
894
895template <class TYPE>
896inline
897bsls::Types::Uint64 BoundedQueue<TYPE>::markFinishedOperation(
898 AtomicUint64 *count,
899 int num)
900{
901 return AtomicOp::addUint64NvAcqRel(count, num * k_FINISHED_INC);
902}
903
904template <class TYPE>
905inline
906void BoundedQueue<TYPE>::markReclaimed(AtomicUint64 *count)
907{
908 AtomicOp::addUint64AcqRel(count, k_STARTED_INC + k_FINISHED_INC);
909}
910
911template <class TYPE>
912inline
913void BoundedQueue<TYPE>::markStartedOperation(AtomicUint64 *count)
914{
915 AtomicOp::addUint64AcqRel(count, k_STARTED_INC);
916}
917
918template <class TYPE>
919inline
920void BoundedQueue<TYPE>::markStartedOperation(AtomicUint64 *count, int num)
921{
922 AtomicOp::addUint64AcqRel(count, num * k_STARTED_INC);
923}
924
925template <class TYPE>
926inline
927bsls::Types::Uint64 BoundedQueue<TYPE>::unmarkStartedOperation(
928 AtomicUint64 *count)
929{
930 return AtomicOp::addUint64NvAcqRel(count, k_STARTED_DEC);
931}
932
933// PRIVATE MANIPULATORS
934template <class TYPE>
935inline
936void BoundedQueue<TYPE>::popComplete(Node *node)
937{
938 node->d_value.object().~TYPE();
939
940 Uint64 count = markFinishedOperation(&d_popCount);
941 if (isQuiescentState(count)) {
942
943 // The total number of popped elements is 'count & k_STARTED_MASK'.
944 // Attempt, once, to zero the count and, if successful, post to the
945 // push semaphore.
946
947 if (AtomicOp::testAndSwapUint64AcqRel(&d_popCount,
948 count,
949 0) == count) {
950 d_pushSemaphore.postWithRedundantSignal(
951 static_cast<int>(count & k_STARTED_MASK),
952 static_cast<int>(d_capacity),
953 1);
954
955 Uint emptyCount = AtomicOp::getUintAcquire(&d_emptyWaiterCount);
956
957 if (isEmpty() && updateEmptyCountSeen(emptyCount)) {
958 {
959 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
960 }
961 d_emptyCondition.broadcast();
962 }
963 }
964 }
965}
966
967template <class TYPE>
968void BoundedQueue<TYPE>::popFrontHelper(TYPE *value)
969{
970 markStartedOperation(&d_popCount);
971
972 // 'd_popIndex' stores the next location to use (want the original value)
973
974 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_popIndex, 1) - 1)
975 % d_capacity;
976 Node *node = &d_element_p[index];
977
978 // Nodes marked for reclamation are not counted in 'd_popSemaphore' and are
979 // to be skipped; 'd_isUnconstructed' does not need to be modified here
980 // since it will be updated in a "push" operation. However, the node does
981 // need to be counted in the 'd_pushSemaphore' as an empty node. This is
982 // accomplished by incrementing the started and finished attributes in
983 // 'd_popCount'.
984
985 while (node->isUnconstructed()) {
986 markReclaimed(&d_popCount);
987
988 index = (AtomicOp::addUint64NvAcqRel(&d_popIndex, 1) - 1) % d_capacity;
989 node = &d_element_p[index];
990 }
991
992 BoundedQueue_PopCompleteGuard<BoundedQueue<TYPE>, Node> guard(this, node);
993
994#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
995 *value = bslmf::MovableRefUtil::move(node->d_value.object());
996#else
997 *value = node->d_value.object();
998#endif
999}
1000
1001template <class TYPE>
1002inline
1003void BoundedQueue<TYPE>::pushComplete()
1004{
1005 Uint64 count = markFinishedOperation(&d_pushCount);
1006 if (isQuiescentState(count)) {
1007
1008 // The total number of pushed elements is 'count & k_STARTED_MASK'.
1009 // Attempt, once, to zero the count and, if successful, post to the pop
1010 // semaphore.
1011
1012 if (AtomicOp::testAndSwapUint64AcqRel(&d_pushCount,
1013 count,
1014 0) == count) {
1015 d_popSemaphore.postWithRedundantSignal(
1016 static_cast<int>(count & k_STARTED_MASK),
1017 static_cast<int>(d_capacity),
1018 1);
1019 }
1020 }
1021}
1022
1023template <class TYPE>
1024inline
1025void BoundedQueue<TYPE>::pushExceptionComplete()
1026{
1027 Uint64 count = unmarkStartedOperation(&d_pushCount);
1028
1029 int numToPost = static_cast<int>(count & k_STARTED_MASK);
1030
1031 if (0 != numToPost && isQuiescentState(count)) {
1032
1033 // The total number of pushed elements is 'count & k_STARTED_MASK'.
1034 // Attempt, once, to zero the count and, if successful, post to the pop
1035 // semaphore.
1036
1037 if (AtomicOp::testAndSwapUint64AcqRel(&d_pushCount,
1038 count,
1039 0) == count) {
1040 d_popSemaphore.post(numToPost);
1041 }
1042 }
1043}
1044
1045template <class TYPE>
1046inline
1047bool BoundedQueue<TYPE>::updateEmptyCountSeen(Uint emptyCount)
1048{
1049 Uint emptyCountSeen = AtomicOp::getUintAcquire(&d_emptyCountSeen);
1050 while (circularlyGreater(emptyCount, emptyCountSeen)) {
1051 const Uint origEmptyCountSeen = emptyCountSeen;
1052
1053 emptyCountSeen = AtomicOp::testAndSwapUintAcqRel(&d_emptyCountSeen,
1054 emptyCountSeen,
1055 emptyCount);
1056
1057 if (origEmptyCountSeen == emptyCountSeen) {
1058 return true; // RETURN
1059 }
1060 }
1061 return false;
1062}
1063
1064// CREATORS
1065template <class TYPE>
1067 bslma::Allocator *basicAllocator)
1068: d_pushSemaphore()
1069, d_popSemaphore()
1070, d_emptyMutex()
1071, d_emptyCondition()
1072, d_element_p(0)
1073, d_capacity(capacity > 2 ? capacity : 2)
1074, d_allocator_p(bslma::Default::allocator(basicAllocator))
1075{
1076 AtomicOp::initUint64(&d_pushCount, 0);
1077 AtomicOp::initUint64(&d_pushIndex, 0);
1078 AtomicOp::initUint64(&d_popCount, 0);
1079 AtomicOp::initUint64(&d_popIndex, 0);
1080
1081 AtomicOp::initUint(&d_emptyWaiterCount, 0);
1082 AtomicOp::initUint(&d_emptyCountSeen, 0);
1083
1084 d_element_p = static_cast<Node *>(
1085 d_allocator_p->allocate(static_cast<bsl::size_t>(
1086 d_capacity * sizeof(Node))));
1087
1088 for (bsl::size_t i = 0; i < d_capacity; ++i) {
1089 d_element_p[i].setIsUnconstructed(false);
1090 }
1091
1092 d_pushSemaphore.post(static_cast<int>(d_capacity));
1093}
1094
1095template <class TYPE>
1097{
1098 if (d_element_p) {
1099 removeAll();
1100 d_allocator_p->deallocate(d_element_p);
1101 }
1102}
1103
1104// MANIPULATORS
1105template <class TYPE>
1106inline
1108{
1109 int rv = d_popSemaphore.wait();
1110 if (rv) {
1112 return e_DISABLED; // RETURN
1113 }
1114 return e_FAILED; // RETURN
1115 }
1116
1117 popFrontHelper(value);
1118
1119 return e_SUCCESS;
1120}
1121
1122template <class TYPE>
1123int BoundedQueue<TYPE>::pushBack(const TYPE& value)
1124{
1125 int rv = d_pushSemaphore.wait();
1126 if (rv) {
1128 return e_DISABLED; // RETURN
1129 }
1130 return e_FAILED; // RETURN
1131 }
1132
1133 markStartedOperation(&d_pushCount);
1134
1135 // 'd_pushIndex' stores the next location to use (want the original value)
1136
1137 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1138 % d_capacity;
1139 Node& node = d_element_p[index];
1140
1141 node.setIsUnconstructed(true);
1142
1144
1145 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
1146 value,
1147 d_allocator_p);
1148
1149 guard.release();
1150
1151 node.setIsUnconstructed(false);
1152
1153 pushComplete();
1154
1155 return e_SUCCESS;
1156}
1157
1158template <class TYPE>
1160{
1161 int rv = d_pushSemaphore.wait();
1162 if (rv) {
1164 return e_DISABLED; // RETURN
1165 }
1166 return e_FAILED; // RETURN
1167 }
1168
1169 markStartedOperation(&d_pushCount);
1170
1171 // 'd_pushIndex' stores the next location to use (want the original value)
1172
1173 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1174 % d_capacity;
1175 Node& node = d_element_p[index];
1176
1177 node.setIsUnconstructed(true);
1178
1180
1181 TYPE& dummy = value;
1182 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
1183 dummy,
1184 d_allocator_p);
1185
1186 guard.release();
1187
1188 node.setIsUnconstructed(false);
1189
1190 pushComplete();
1191
1192 return e_SUCCESS;
1193}
1194
1195template <class TYPE>
1197{
1198 int reclaim = d_popSemaphore.takeAll();
1199
1200 if (reclaim) {
1201 while (reclaim) {
1202 int count = reclaim;
1203
1204 reclaim = 0;
1205
1206 // For quiescent state detection (see *Implementation* *Note*) and
1207 // eventual 'post' to the 'd_pushSemaphore' to indicate node
1208 // availability, indicate 'count' remove operations have begin.
1209
1210 markStartedOperation(&d_popCount, count);
1211
1212 // 'd_popIndex' stores the next location to use (want the original
1213 // value)
1214
1215 Uint64 index = AtomicOp::addUint64NvAcqRel(&d_popIndex, count)
1216 - count;
1217
1218 for (int i = 0; i < count; ++i, ++index) {
1219 Node& node = d_element_p[index % d_capacity];
1220
1221 if (!node.isUnconstructed()) {
1222 node.d_value.object().~TYPE();
1223 }
1224 else {
1225 ++reclaim;
1226 }
1227 }
1228
1229 // For quiescent state detection (see *Implementation* *Note*) and
1230 // eventual 'post' to the 'd_pushSemaphore' to indicate node
1231 // availability, indicate 'count' remove operations have finished.
1232
1233 Uint64 popCount = markFinishedOperation(&d_popCount, count);
1234
1235 if (isQuiescentState(popCount)) {
1236 // The total number of popped elements is
1237 // 'popCount & k_STARTED_MASK'. Attempt, once, to zero the
1238 // count and, if successful, post to the push semaphore.
1239
1240 if (AtomicOp::testAndSwapUint64AcqRel(&d_popCount,
1241 popCount,
1242 0) == popCount) {
1243 d_pushSemaphore.post(static_cast<int>(
1244 popCount & k_STARTED_MASK));
1245 }
1246
1247 Uint emptyCount = AtomicOp::getUintAcquire(
1248 &d_emptyWaiterCount);
1249
1250 if (isEmpty() && updateEmptyCountSeen(emptyCount)) {
1251 {
1252 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1253 }
1254 d_emptyCondition.broadcast();
1255 }
1256 }
1257 }
1258 }
1259}
1260
1261template <class TYPE>
1262inline
1264{
1265 int rv = d_popSemaphore.tryWait();
1266 if (rv) {
1268 return e_DISABLED; // RETURN
1269 }
1271 return e_EMPTY; // RETURN
1272 }
1273 return e_FAILED; // RETURN
1274 }
1275
1276 popFrontHelper(value);
1277
1278 return e_SUCCESS;
1279}
1280
1281template <class TYPE>
1283{
1284 int rv = d_pushSemaphore.tryWait();
1285 if (rv) {
1287 return e_DISABLED; // RETURN
1288 }
1290 return e_FULL; // RETURN
1291 }
1292 return e_FAILED; // RETURN
1293 }
1294
1295 markStartedOperation(&d_pushCount);
1296
1297 // 'd_pushIndex' stores the next location to use (want the original value)
1298
1299 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1300 % d_capacity;
1301 Node& node = d_element_p[index];
1302
1303 node.setIsUnconstructed(true);
1304
1306
1307 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
1308 value,
1309 d_allocator_p);
1310
1311 guard.release();
1312
1313 node.setIsUnconstructed(false);
1314
1315 pushComplete();
1316
1317 return e_SUCCESS;
1318}
1319
1320template <class TYPE>
1322{
1323 int rv = d_pushSemaphore.tryWait();
1324 if (rv) {
1326 return e_DISABLED; // RETURN
1327 }
1329 return e_FULL; // RETURN
1330 }
1331 return e_FAILED; // RETURN
1332 }
1333
1334 markStartedOperation(&d_pushCount);
1335
1336 // 'd_pushIndex' stores the next location to use (want the original value)
1337
1338 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1339 % d_capacity;
1340 Node& node = d_element_p[index];
1341
1342 node.setIsUnconstructed(true);
1343
1345
1346 TYPE& dummy = value;
1347 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
1348 dummy,
1349 d_allocator_p);
1350
1351 guard.release();
1352
1353 node.setIsUnconstructed(false);
1354
1355 pushComplete();
1356
1357 return e_SUCCESS;
1358}
1359
1360 // Enqueue/Dequeue State
1361
1362template <class TYPE>
1363inline
1365{
1366 d_popSemaphore.disable();
1367
1368 {
1369 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1370 }
1371 d_emptyCondition.broadcast();
1372}
1373
1374template <class TYPE>
1375inline
1377{
1378 d_pushSemaphore.disable();
1379}
1380
1381template <class TYPE>
1382inline
1384{
1385 d_popSemaphore.enable();
1386}
1387
1388template <class TYPE>
1389inline
1391{
1392 d_pushSemaphore.enable();
1393}
1394
1395// ACCESSORS
1396template <class TYPE>
1397inline
1399{
1400 return static_cast<bsl::size_t>(d_capacity);
1401}
1402
1403template <class TYPE>
1404inline
1406{
1407 return d_capacity == static_cast<Uint64>(d_pushSemaphore.getValue());
1408}
1409
1410template <class TYPE>
1411inline
1413{
1414 return 0 == d_pushSemaphore.getValue();
1415}
1416
1417template <class TYPE>
1418inline
1420{
1421 return d_popSemaphore.isDisabled();
1422}
1423
1424template <class TYPE>
1425inline
1427{
1428 return d_pushSemaphore.isDisabled();
1429}
1430
1431template <class TYPE>
1432inline
1434{
1435 return d_popSemaphore.getValue();
1436}
1437
1438template <class TYPE>
1440{
1441 Uint emptyCount = AtomicOp::addUintNvAcqRel(&d_emptyWaiterCount, 1) - 1;
1442
1443 int state = d_popSemaphore.getDisabledState();
1444 if (1 == (state & 1)) {
1445 return e_DISABLED; // RETURN
1446 }
1447
1448 if (isEmpty()) {
1449 return e_SUCCESS; // RETURN
1450 }
1451
1452 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1453
1454 // Return successfully when this queue is empty ('isEmpty()') or this queue
1455 // was empty at some point since this method was invoked (the condition
1456 // tested by 'circularlyGreater' in the below).
1457
1458 bool empty = isEmpty()
1459 || circularlyGreater(AtomicOp::getUintAcquire(&d_emptyCountSeen),
1460 emptyCount);
1461
1462 while (!empty && state == d_popSemaphore.getDisabledState()) {
1463 int rv = d_emptyCondition.wait(&d_emptyMutex);
1464 if (rv) {
1465 return e_FAILED; // RETURN
1466 }
1467 empty = isEmpty()
1468 || circularlyGreater(AtomicOp::getUintAcquire(&d_emptyCountSeen),
1469 emptyCount);
1470 }
1471
1472 if (!empty) {
1473 return e_DISABLED; // RETURN
1474 }
1475
1476 return e_SUCCESS;
1477}
1478
1479 // Aspects
1480
1481template <class TYPE>
1482inline
1484{
1485 return d_allocator_p;
1486}
1487
1488} // close package namespace
1489
1490
1491#endif
1492
1493// ----------------------------------------------------------------------------
1494// Copyright 2019 Bloomberg Finance L.P.
1495//
1496// Licensed under the Apache License, Version 2.0 (the "License");
1497// you may not use this file except in compliance with the License.
1498// You may obtain a copy of the License at
1499//
1500// http://www.apache.org/licenses/LICENSE-2.0
1501//
1502// Unless required by applicable law or agreed to in writing, software
1503// distributed under the License is distributed on an "AS IS" BASIS,
1504// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1505// See the License for the specific language governing permissions and
1506// limitations under the License.
1507// ----------------------------- END-OF-FILE ----------------------------------
1508
1509/** @} */
1510/** @} */
1511/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlcc_boundedqueue.h:281
~BoundedQueue_PopCompleteGuard()
Definition bdlcc_boundedqueue.h:796
Definition bdlcc_boundedqueue.h:314
void release()
Definition bdlcc_boundedqueue.h:827
~BoundedQueue_PushExceptionCompleteProctor()
Definition bdlcc_boundedqueue.h:817
Definition bdlcc_boundedqueue.h:418
bsl::size_t capacity() const
Definition bdlcc_boundedqueue.h:1398
bool isPopFrontDisabled() const
Definition bdlcc_boundedqueue.h:1419
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_boundedqueue.h:1159
int waitUntilEmpty() const
Definition bdlcc_boundedqueue.h:1439
void enablePushBack()
Definition bdlcc_boundedqueue.h:1390
BoundedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_boundedqueue.h:1066
void removeAll()
Definition bdlcc_boundedqueue.h:1196
void enablePopFront()
Definition bdlcc_boundedqueue.h:1383
int tryPopFront(TYPE *value)
Definition bdlcc_boundedqueue.h:1263
int popFront(TYPE *value)
Definition bdlcc_boundedqueue.h:1107
bool isEmpty() const
Definition bdlcc_boundedqueue.h:1405
void disablePopFront()
Definition bdlcc_boundedqueue.h:1364
int pushBack(const TYPE &value)
Definition bdlcc_boundedqueue.h:1123
bsl::size_t numElements() const
Definition bdlcc_boundedqueue.h:1433
bool isFull() const
Definition bdlcc_boundedqueue.h:1412
bool isPushBackDisabled() const
Definition bdlcc_boundedqueue.h:1426
~BoundedQueue()
Destroy this object.
Definition bdlcc_boundedqueue.h:1096
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_boundedqueue.h:1483
int tryPushBack(const TYPE &value)
Definition bdlcc_boundedqueue.h:1282
void disablePushBack()
Definition bdlcc_boundedqueue.h:1376
TYPE value_type
Definition bdlcc_boundedqueue.h:622
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_boundedqueue.h:1321
Definition bslma_allocator.h:545
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:752
Definition bslmt_condition.h:220
Definition bslmt_fastpostsemaphore.h:328
void post()
Atomically increment the count of this semaphore.
Definition bslmt_fastpostsemaphore.h:610
@ e_DISABLED
Definition bslmt_fastpostsemaphore.h:349
@ e_WOULD_BLOCK
Definition bslmt_fastpostsemaphore.h:354
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
bsls::ObjectBuffer< TYPE > d_value
Definition bdlcc_boundedqueue.h:397
bsls::ObjectBuffer< TYPE > d_value
Definition bdlcc_boundedqueue.h:375
Definition bdlcc_boundedqueue.h:365
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
Definition bslmf_isbitwisecopyable.h:298
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
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