BDE 4.14.0 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 // NOT IMPLEMENTED
292
293 public:
294 // CREATORS
295
296 /// Create a `popComplete` guard managing the specified `queue` and `node`.
297 BoundedQueue_PopCompleteGuard(TYPE *queue, NODE *node);
298
299 /// Destroy this object and invoke the `TYPE::popComplete` method with the
300 /// managed `node`.
302};
303
304 // ===============================================
305 // class BoundedQueue_PushExceptionCompleteProctor
306 // ===============================================
307
308/// This class implements a proctor that invokes `TYPE::pushExceptionComplete`
309/// upon destruction unless `release` has been called.
310///
311/// See @ref bdlcc_boundedqueue
312template <class TYPE>
314
315 // DATA
316 TYPE *d_queue_p; // managed queue
317
318 // NOT IMPLEMENTED
324
325 public:
326 // CREATORS
327
328 /// Create a `pushExceptionComplete` proctor that conditionally manages the
329 /// specified `queue` (if non-zero).
330 explicit
332
333 /// Destroy this object and, if `release` has not been invoked', invoke the
334 /// managed queue's `pushExceptionComplete` method.
336
337 // MANIPULATORS
338
339 /// Release from management the queue currently managed by this proctor.
340 /// If no queue is currently managed, this method has no effect.
341 void release();
342};
343
344 // ========================
345 // struct BoundedQueue_Node
346 // ========================
347
348/// This class implements the queue's node. A node stores an instance of the
349/// specified (template parameter) `TYPE`, and provides an accessor
350/// `isUnconstructed` that indicates whether the value of the node was
351/// correctly constructed. If `isUnconstructed` is `false`, then the value
352/// (`d_value`) refers to a valid object. If `isUnconstructed` is `true` then
353/// `d_value` does not refer to a valid object, it does not represent a value
354/// in this queue, and the destructor of `d_value` should not be called. The
355/// specified (template parameter) type `RECLAIMABLE` is used to provide a
356/// compile time optimization for the footprint of this template when the value
357/// of `isUnconstructed` is known at compile-time. If `RECLAIMABLE` is `false`
358/// then it can be determined at compile time that the construction of `TYPE`
359/// will uncoditionally succeed (e.g., it `IsBitwiseCopyable`), and the
360/// `isUnconstructed` property does not require a data member to be accessed at
361/// run-time.
362template <class TYPE, bool RECLAIMABLE>
364
365template <class TYPE>
366struct BoundedQueue_Node<TYPE, true> {
367 private:
368 // DATA
369 bool d_isUnconstructedFlag; // node suffered exception
370
371 public:
372 // PUBLIC DATA
374
375 // MANIPULATORS
376
377 /// If the specified `isUnconstructedFlag` is `false`, then `d_value`
378 /// refers to a valid object of (the template parameter) `TYPE`, otherwise
379 /// (if `isUnconstrucedFlag` is `true`) `d_value` does not refer to a valid
380 /// object (because the attempt to construct `TYPE` resulted in an
381 /// expection). Note that this method is normally invoked after each write
382 /// to `d_value`.
383 void setIsUnconstructed(bool isUnconstructedFlag);
384
385 // ACCESSORS
386
387 /// Return whether an exception occurred when last writing to `d_value`.
388 bool isUnconstructed() const;
389};
390
391template <class TYPE>
392struct BoundedQueue_Node<TYPE, false> {
393 // PUBLIC DATA
395
396 // MANIPULATORS
397
398 /// Do nothing.
399 void setIsUnconstructed(bool /* value */);
400
401 // ACCESSORS
402
403 /// Return `false`.
404 bool isUnconstructed() const;
405};
406
407 // ==================
408 // class BoundedQueue
409 // ==================
410
411/// This class provides a thread-safe bounded queue of values.
412///
413/// See @ref bdlcc_boundedqueue
414template <class TYPE>
416
417 // PRIVATE CONSTANTS
418
419 // The following constants are used to maintain the queue's `d_popCount`
420 // and `d_pushCount` values. See *Implementation* *Note* for details.
421
422 static const bsls::Types::Uint64 k_STARTED_MASK = 0x00000000ffffffffLL;
423 static const bsls::Types::Uint64 k_STARTED_INC = 0x0000000000000001LL;
424 static const bsls::Types::Uint64 k_STARTED_DEC = 0xffffffffffffffffLL;
425 static const bsls::Types::Uint64 k_FINISHED_INC = 0x0000000100000000LL;
426 static const unsigned int k_FINISHED_SHIFT = 32;
427
428 static const unsigned int k_MAXIMUM_CIRCULAR_DIFFERENCE =
429 static_cast<unsigned int>(1) << (sizeof(unsigned int) * CHAR_BIT - 1);
430
431 // PRIVATE TYPES
432 typedef unsigned int Uint;
433 typedef typename bsls::Types::Uint64 Uint64;
434 typedef typename bsls::AtomicOperations::AtomicTypes::Uint AtomicUint;
435 typedef typename bsls::AtomicOperations::AtomicTypes::Uint64 AtomicUint64;
436
437 typedef typename bsls::AtomicOperations AtomicOp;
438
439 typedef BoundedQueue_Node<TYPE,
441
442 // DATA
443 bslmt::FastPostSemaphore d_pushSemaphore; // synchronization primitive
444 // restricting access to
445 // empty elements and
446 // providing
447 // enablement/disablement of
448 // "push" operations
449
450 AtomicUint64 d_pushCount; // count of "push"
451 // operations started and
452 // completed, used to detect
453 // a quiescent state for
454 // safely incrementing
455 // number of available
456 // elements
457
458 AtomicUint64 d_pushIndex; // index of next enqueue
459 // element location
460
461 bslmt::FastPostSemaphore d_popSemaphore; // synchronization primitive
462 // restricting access to
463 // available elements and
464 // providing
465 // enablement/disablement of
466 // "pop" operations
467
468 AtomicUint64 d_popCount; // count of "pop" operations
469 // started and completed,
470 // used to detect a
471 // quiescent state for
472 // safely incrementing
473 // number of empty elements
474
475 AtomicUint64 d_popIndex; // index of next dequeue
476 // element location
477
478 mutable AtomicUint d_emptyWaiterCount; // circular count of
479 // `waitUntilEmpty`
480 // invocations
481
482 AtomicUint d_emptyCountSeen; // maximum
483 // `d_emptyWaiterCount` seen
484 // prior to the queue being
485 // observed as empty; used
486 // to detect most very short
487 // lived transitions to the
488 // queue being empty
489
490 mutable bslmt::Mutex d_emptyMutex; // blocking point for
491 // `waitUntilEmpty`
492
493 mutable bslmt::Condition d_emptyCondition; // condition variable for
494 // `waitUntilEmpty`
495
496 Node *d_element_p; // array of elements that
497 // comprise the bounded
498 // queue
499
500 const Uint64 d_capacity; // capacity of the queue
501
502 bslma::Allocator *d_allocator_p; // allocator, held not owned
503
504 // FRIENDS
506 BoundedQueue<TYPE>,
507 typename BoundedQueue<TYPE>::Node>;
508
510 BoundedQueue<TYPE> >;
511
512 // PRIVATE CLASS METHODS
513
514 /// Return `true` if the specified `lhs` is circularly greater than the
515 /// specified `rhs`, and `false` otherwise. `lhs` is cicularly greater
516 /// than `rhs` if `lhs` is equal to a value obtained by adding a value
517 /// in `[1 .. 2^31]` to `rhs`.
518 static bool circularlyGreater(Uint lhs, Uint rhs);
519
520 /// Return `true` if the specified `count` implies a quiescent state (see
521 /// **Implementation Note**), and `false` otherwise. A quiescent state
522 /// indicates there is a (possibly zero length) contiguous set of elements
523 /// that can safely be made available to the operation complementary to the
524 /// operation `count` tracks (pop and push are complementary operations).
525 static bool isQuiescentState(bsls::Types::Uint64 count);
526
527 static Uint64 markFinishedOperation(AtomicUint64 *count);
528 /// Update the specified `count` to indicate that an operation (or the
529 /// optionally specified `num` operations) has completed, and return the
530 /// updated `count` value. Marking an operation finished means that
531 /// `isQuiscentState` **may** now be true. The behavior is undefined
532 /// unless `markStartedOperation` was previously called on this `count`,
533 /// and a corresponding `markFinishedOperation` or `unmarkStartOperation`
534 /// has not already been called. Note that the "operation" that has
535 /// finished refers to either a push or pop (depending on whether this is
536 /// applied to `d_pushCount` or `d_popCount`).
537 static Uint64 markFinishedOperation(AtomicUint64 *count, int num);
538
539 /// Update the specified `count` to indicate that a node that suffered an
540 /// exception has been reclaimed. Marking a node reclaimed can *not* alter
541 /// the value of `isQuiescentState`, but does increase the size of the
542 /// contiguous set of elements that will eventially be made available to
543 /// the operation complementary to the operation `count` tracks (pop and
544 /// push are complementary operations). Note that this method does not
545 /// require a previous call to `markStartedOperation` and does not meet the
546 /// requirements for `markFinishedOperation` or `unmarkStartOperation` to
547 /// be invoked.
548 static void markReclaimed(AtomicUint64 *count);
549
550 static void markStartedOperation(AtomicUint64 *count);
551 /// Update the specified `count` to indicate that an operation (or the
552 /// optionally specified `num` operations) has started, and return the
553 /// updated `count` value. Marking an operation started means that
554 /// `isQuiescentState` is not true and will not be true until
555 /// `markFinishedOperation` or `unmarkStartedOperation` is invoked. Note
556 /// that the "operation" that has started refers to either a push or pop
557 /// (depending on whether this is applied to `d_pushCount` or
558 /// `d_popCount`).
559 static void markStartedOperation(AtomicUint64 *count, int num);
560
561 /// Update the specified `count` to indicate that an operation has aborted
562 /// without finishing, and return the updated `count` value. Marking a
563 /// started operation as having aborted means that `isQuiscentState` *may*
564 /// now be true. The behavior is undefined unless `markStartedOperation`
565 /// was previously called on this `count`, and a corresponding
566 /// `markFinishedOperation` or `unmarkStartOperation` has not already been
567 /// called. Note that the "operation" that has aborted refers to either a
568 /// push or pop (depending on whether this is applied to `d_pushCount` or
569 /// `d_popCount`).
570 static Uint64 unmarkStartedOperation(AtomicUint64 *count);
571
572 // PRIVATE MANIPULATORS
573
574 /// Destruct the value stored in the specified `node`, and mark the `node`
575 /// writable. This method is used within `popFrontHelper` by a guard to
576 /// complete the reclamation of a node in the presence of an exception.
577 void popComplete(Node *node);
578
579 /// Remove the element from the front of this queue and load that element
580 /// into the specified `value`. This method is invoked by `popFront` and
581 /// `tryPopFront` once an element is available.
582 void popFrontHelper(TYPE *value);
583
584 /// Mark a "push" operation as complete, and `post` to the `d_popSemaphore`
585 /// if appropriate.
586 void pushComplete();
587
588 /// Remove the indicator for a started push operation, and `post` to the
589 /// `d_popSemaphore` if appropriate. This method is used within
590 /// `pushFront` by a proctor to complete the marking of a node to reclaim
591 /// in the presence of an exception.
592 void pushExceptionComplete();
593
594 /// If the specified `emptyCount` is (circularly) greater than
595 /// `d_emptyCountSeen`, assign `d_emptyCountSeen` the value of `emptyCount`
596 /// and return `true`. Otherwise, return `false`. A return value of
597 /// `true` indicates this thread *must* signal any waiting threads. Note
598 /// that a return value of `false` indicates another thread has (or will)
599 /// signal the queue is empty and this thread does not need to signal.
600 bool updateEmptyCountSeen(Uint emptyCount);
601
602 // NOT IMPLEMENTED
603 BoundedQueue(const BoundedQueue&);
604 BoundedQueue& operator=(const BoundedQueue&);
605
606 public:
607 // TRAITS
608 BSLMF_NESTED_TRAIT_DECLARATION(BoundedQueue, bslma::UsesBslmaAllocator);
609
610 // PUBLIC TYPES
611 typedef TYPE value_type; // The type for elements.
612
613 // PUBLIC CONSTANTS
614 enum {
615 e_SUCCESS = 0, // must be 0
616 e_EMPTY = -1,
617 e_FULL = -2,
618 e_DISABLED = -3,
619 e_FAILED = -4
620 };
621
622 // CREATORS
623
624 /// Create a thread-aware queue with at least the specified `capacity`.
625 /// Optionally specify a `basicAllocator` used to supply memory. If
626 /// `basicAllocator` is 0, the currently installed default allocator is
627 /// used.
628 explicit
629 BoundedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
630
631 /// Destroy this object.
633
634 // MANIPULATORS
635
636 /// Remove the element from the front of this queue and load that
637 /// element into the specified `value`. If the queue is empty, block
638 /// until it is not empty. Return 0 on success, and a non-zero value
639 /// otherwise. Specifically, return `e_SUCCESS` on success,
640 /// `e_DISABLED` if `isPopFrontDisabled()` and `e_FAILED` if an error
641 /// occurs. On failure, `value` is not changed. Threads blocked due to
642 /// the queue being empty will return `e_DISABLED` if `disablePopFront`
643 /// is invoked.
644 int popFront(TYPE *value);
645
646 /// Append the specified `value` to the back of this queue. If the
647 /// queue is full, block until it is not full. Return 0 on success, and
648 /// a non-zero value otherwise. Specifically, return `e_SUCCESS` on
649 /// success, `e_DISABLED` if `isPushBackDisabled()` and `e_FAILED` if an
650 /// error occurs. Threads blocked due to the queue being full will
651 /// return `e_DISABLED` if `disablePushBack` is invoked.
652 int pushBack(const TYPE& value);
653
654 /// Append the specified move-insertable `value` to the back of this
655 /// queue. If the queue is full, block until it is not full. `value`
656 /// is left in a valid but unspecified state. Return 0 on success, and
657 /// a non-zero value otherwise. Specifically, return `e_SUCCESS` on
658 /// success, `e_DISABLED` if `isPushBackDisabled()` and `e_FAILED` if an
659 /// error occurs. On failure, `value` is not changed. Threads blocked
660 /// due to the queue being full will return `e_DISABLED` if
661 /// `disablePushBack` is invoked.
663
664 /// Remove all items currently in this queue. Note that this operation
665 /// is not atomic; if other threads are concurrently pushing items into
666 /// the queue the result of `numElements()` after this function returns
667 /// is not guaranteed to be 0.
668 void removeAll();
669
670 /// Attempt to remove the element from the front of this queue without
671 /// blocking, and, if successful, load the specified `value` with the
672 /// removed element. Return 0 on success, and a non-zero value
673 /// otherwise. Specifically, return `e_SUCCESS` on success,
674 /// `e_DISABLED` if `isPopFrontDisabled()`, `e_EMPTY` if
675 /// `!isPopFrontDisabled()` and the queue was empty, and `e_FAILED` if
676 /// an error occurs. On failure, `value` is not changed.
677 int tryPopFront(TYPE *value);
678
679 /// Append the specified `value` to the back of this queue. Return 0 on
680 /// success, and a non-zero value otherwise. Specifically, return
681 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`,
682 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full, and
683 /// `e_FAILED` if an error occurs.
684 int tryPushBack(const TYPE& value);
685
686 /// Append the specified move-insertable `value` to the back of this
687 /// queue. `value` is left in a valid but unspecified state. Return 0
688 /// on success, and a non-zero value otherwise. Specifically, return
689 /// `e_SUCCESS` on success, `e_DISABLED` if `isPushBackDisabled()`,
690 /// `e_FULL` if `!isPushBackDisabled()` and the queue was full, and
691 /// `e_FAILED` if an error occurs. On failure, `value` is not changed.
693
694 // Enqueue/Dequeue State
695
696 /// Disable dequeueing from this queue. All subsequent invocations of
697 /// `popFront` or `tryPopFront` will fail immediately. All blocked
698 /// invocations of `popFront` and `waitUntilEmpty` will fail
699 /// immediately. If the queue is already dequeue disabled, this method
700 /// has no effect.
702
703 /// Disable enqueueing into this queue. All subsequent invocations of
704 /// `pushBack` or `tryPushBack` will fail immediately. All blocked
705 /// invocations of `pushBack` will fail immediately. If the queue is
706 /// already enqueue disabled, this method has no effect.
708
709 /// Enable dequeueing. If the queue is not dequeue disabled, this call
710 /// has no effect.
712
713 /// Enable queuing. If the queue is not enqueue disabled, this call has
714 /// no effect.
716
717 // ACCESSORS
718
719 /// Return the maximum number of elements that may be stored in this queue.
720 /// Note that the value returned may be greater than that supplied at
721 /// construction.
722 bsl::size_t capacity() const;
723
724 /// Return `true` if this queue is empty (has no elements), or `false`
725 /// otherwise.
726 bool isEmpty() const;
727
728 /// Return `true` if this queue is full (has no available capacity), or
729 /// `false` otherwise. Note that for unbounded queues, this method always
730 /// returns `false`.
731 bool isFull() const;
732
733 /// Return `true` if this queue is dequeue disabled, and `false` otherwise.
734 /// Note that the queue is created in the "dequeue enabled" state.
735 bool isPopFrontDisabled() const;
736
737 /// Return `true` if this queue is enqueue disabled, and `false` otherwise.
738 /// Note that the queue is created in the "enqueue enabled" state.
739 bool isPushBackDisabled() const;
740
741 /// Returns the number of elements currently in this queue. Note that
742 /// `numElements() == capacity()` is not a valid replacement for `isFull`
743 /// (see {Exception Safety} for details).
744 bsl::size_t numElements() const;
745
746 /// Block until all the elements in this queue are removed. Return 0 on
747 /// success, and a non-zero value otherwise. Specifically, return
748 /// `e_SUCCESS` on success, `e_DISABLED` if `!isEmpty() &&
749 /// isPopFrontDisabled()`. A blocked thread waiting for the queue to empty
750 /// will return `e_DISABLED` if `disablePopFront` is invoked.
751 int waitUntilEmpty() const;
752
753 // Aspects
754
755 /// Return the allocator used by this object to supply memory.
757};
758
759// ============================================================================
760// INLINE DEFINITIONS
761// ============================================================================
762
763 // -----------------------------------
764 // class BoundedQueue_PopCompleteGuard
765 // -----------------------------------
766
767// CREATORS
768template <class TYPE, class NODE>
769inline
771 BoundedQueue_PopCompleteGuard(TYPE *queue, NODE *node)
772: d_queue_p(queue)
773, d_node_p(node)
774{
775}
776
777template <class TYPE, class NODE>
778inline
783
784 // -----------------------------------------------
785 // class BoundedQueue_PushExceptionCompleteProctor
786 // -----------------------------------------------
787
788// CREATORS
789template <class TYPE>
790inline
796
797template <class TYPE>
798inline
801{
802 if (d_queue_p) {
803 d_queue_p->pushExceptionComplete();
804 }
805}
806
807// MANIPULATORS
808template <class TYPE>
809inline
814
815 // ------------------------
816 // struct BoundedQueue_Node
817 // ------------------------
818
819// MANIPULATORS
820template <class TYPE>
821inline
823 bool isUnconstructedFlag)
824{
825 d_isUnconstructedFlag = isUnconstructedFlag;
826}
827
828template <class TYPE>
829inline
831 bool /* isUnconstrutedFlag */)
832{
833}
834
835// ACCESSORS
836template <class TYPE>
837inline
839{
840 return d_isUnconstructedFlag;
841}
842
843template <class TYPE>
844inline
846{
847 return false;
848}
849
850 // ------------------
851 // class BoundedQueue
852 // ------------------
853
854// PRIVATE CLASS METHODS
855template <class TYPE>
856inline
857bool BoundedQueue<TYPE>::circularlyGreater(Uint lhs, Uint rhs)
858{
859 return lhs > rhs ? (lhs - rhs) <= k_MAXIMUM_CIRCULAR_DIFFERENCE
860 : (rhs - lhs) > k_MAXIMUM_CIRCULAR_DIFFERENCE;
861}
862
863template <class TYPE>
864inline
865bool BoundedQueue<TYPE>::isQuiescentState(bsls::Types::Uint64 count)
866{
867 return (count >> k_FINISHED_SHIFT) == (count & k_STARTED_MASK);
868}
869
870template <class TYPE>
871inline
872bsls::Types::Uint64 BoundedQueue<TYPE>::markFinishedOperation(
873 AtomicUint64 *count)
874{
875 return AtomicOp::addUint64NvAcqRel(count, k_FINISHED_INC);
876}
877
878template <class TYPE>
879inline
880bsls::Types::Uint64 BoundedQueue<TYPE>::markFinishedOperation(
881 AtomicUint64 *count,
882 int num)
883{
884 return AtomicOp::addUint64NvAcqRel(count, num * k_FINISHED_INC);
885}
886
887template <class TYPE>
888inline
889void BoundedQueue<TYPE>::markReclaimed(AtomicUint64 *count)
890{
891 AtomicOp::addUint64AcqRel(count, k_STARTED_INC + k_FINISHED_INC);
892}
893
894template <class TYPE>
895inline
896void BoundedQueue<TYPE>::markStartedOperation(AtomicUint64 *count)
897{
898 AtomicOp::addUint64AcqRel(count, k_STARTED_INC);
899}
900
901template <class TYPE>
902inline
903void BoundedQueue<TYPE>::markStartedOperation(AtomicUint64 *count, int num)
904{
905 AtomicOp::addUint64AcqRel(count, num * k_STARTED_INC);
906}
907
908template <class TYPE>
909inline
910bsls::Types::Uint64 BoundedQueue<TYPE>::unmarkStartedOperation(
911 AtomicUint64 *count)
912{
913 return AtomicOp::addUint64NvAcqRel(count, k_STARTED_DEC);
914}
915
916// PRIVATE MANIPULATORS
917template <class TYPE>
918inline
919void BoundedQueue<TYPE>::popComplete(Node *node)
920{
921 node->d_value.object().~TYPE();
922
923 Uint64 count = markFinishedOperation(&d_popCount);
924 if (isQuiescentState(count)) {
925
926 // The total number of popped elements is 'count & k_STARTED_MASK'.
927 // Attempt, once, to zero the count and, if successful, post to the
928 // push semaphore.
929
930 if (AtomicOp::testAndSwapUint64AcqRel(&d_popCount,
931 count,
932 0) == count) {
933 d_pushSemaphore.postWithRedundantSignal(
934 static_cast<int>(count & k_STARTED_MASK),
935 static_cast<int>(d_capacity),
936 1);
937
938 Uint emptyCount = AtomicOp::getUintAcquire(&d_emptyWaiterCount);
939
940 if (isEmpty() && updateEmptyCountSeen(emptyCount)) {
941 {
942 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
943 }
944 d_emptyCondition.broadcast();
945 }
946 }
947 }
948}
949
950template <class TYPE>
951void BoundedQueue<TYPE>::popFrontHelper(TYPE *value)
952{
953 markStartedOperation(&d_popCount);
954
955 // 'd_popIndex' stores the next location to use (want the original value)
956
957 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_popIndex, 1) - 1)
958 % d_capacity;
959 Node *node = &d_element_p[index];
960
961 // Nodes marked for reclamation are not counted in 'd_popSemaphore' and are
962 // to be skipped; 'd_isUnconstructed' does not need to be modified here
963 // since it will be updated in a "push" operation. However, the node does
964 // need to be counted in the 'd_pushSemaphore' as an empty node. This is
965 // accomplished by incrementing the started and finished attributes in
966 // 'd_popCount'.
967
968 while (node->isUnconstructed()) {
969 markReclaimed(&d_popCount);
970
971 index = (AtomicOp::addUint64NvAcqRel(&d_popIndex, 1) - 1) % d_capacity;
972 node = &d_element_p[index];
973 }
974
975 BoundedQueue_PopCompleteGuard<BoundedQueue<TYPE>, Node> guard(this, node);
976
977#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
978 *value = bslmf::MovableRefUtil::move(node->d_value.object());
979#else
980 *value = node->d_value.object();
981#endif
982}
983
984template <class TYPE>
985inline
986void BoundedQueue<TYPE>::pushComplete()
987{
988 Uint64 count = markFinishedOperation(&d_pushCount);
989 if (isQuiescentState(count)) {
990
991 // The total number of pushed elements is 'count & k_STARTED_MASK'.
992 // Attempt, once, to zero the count and, if successful, post to the pop
993 // semaphore.
994
995 if (AtomicOp::testAndSwapUint64AcqRel(&d_pushCount,
996 count,
997 0) == count) {
998 d_popSemaphore.postWithRedundantSignal(
999 static_cast<int>(count & k_STARTED_MASK),
1000 static_cast<int>(d_capacity),
1001 1);
1002 }
1003 }
1004}
1005
1006template <class TYPE>
1007inline
1008void BoundedQueue<TYPE>::pushExceptionComplete()
1009{
1010 Uint64 count = unmarkStartedOperation(&d_pushCount);
1011
1012 int numToPost = static_cast<int>(count & k_STARTED_MASK);
1013
1014 if (0 != numToPost && isQuiescentState(count)) {
1015
1016 // The total number of pushed elements is 'count & k_STARTED_MASK'.
1017 // Attempt, once, to zero the count and, if successful, post to the pop
1018 // semaphore.
1019
1020 if (AtomicOp::testAndSwapUint64AcqRel(&d_pushCount,
1021 count,
1022 0) == count) {
1023 d_popSemaphore.post(numToPost);
1024 }
1025 }
1026}
1027
1028template <class TYPE>
1029inline
1030bool BoundedQueue<TYPE>::updateEmptyCountSeen(Uint emptyCount)
1031{
1032 Uint emptyCountSeen = AtomicOp::getUintAcquire(&d_emptyCountSeen);
1033 while (circularlyGreater(emptyCount, emptyCountSeen)) {
1034 const Uint origEmptyCountSeen = emptyCountSeen;
1035
1036 emptyCountSeen = AtomicOp::testAndSwapUintAcqRel(&d_emptyCountSeen,
1037 emptyCountSeen,
1038 emptyCount);
1039
1040 if (origEmptyCountSeen == emptyCountSeen) {
1041 return true; // RETURN
1042 }
1043 }
1044 return false;
1045}
1046
1047// CREATORS
1048template <class TYPE>
1050 bslma::Allocator *basicAllocator)
1051: d_pushSemaphore()
1052, d_popSemaphore()
1053, d_emptyMutex()
1054, d_emptyCondition()
1055, d_element_p(0)
1056, d_capacity(capacity > 2 ? capacity : 2)
1057, d_allocator_p(bslma::Default::allocator(basicAllocator))
1058{
1059 AtomicOp::initUint64(&d_pushCount, 0);
1060 AtomicOp::initUint64(&d_pushIndex, 0);
1061 AtomicOp::initUint64(&d_popCount, 0);
1062 AtomicOp::initUint64(&d_popIndex, 0);
1063
1064 AtomicOp::initUint(&d_emptyWaiterCount, 0);
1065 AtomicOp::initUint(&d_emptyCountSeen, 0);
1066
1067 d_element_p = static_cast<Node *>(
1068 d_allocator_p->allocate(static_cast<bsl::size_t>(
1069 d_capacity * sizeof(Node))));
1070
1071 for (bsl::size_t i = 0; i < d_capacity; ++i) {
1072 d_element_p[i].setIsUnconstructed(false);
1073 }
1074
1075 d_pushSemaphore.post(static_cast<int>(d_capacity));
1076}
1077
1078template <class TYPE>
1080{
1081 if (d_element_p) {
1082 removeAll();
1083 d_allocator_p->deallocate(d_element_p);
1084 }
1085}
1086
1087// MANIPULATORS
1088template <class TYPE>
1089inline
1091{
1092 int rv = d_popSemaphore.wait();
1093 if (rv) {
1095 return e_DISABLED; // RETURN
1096 }
1097 return e_FAILED; // RETURN
1098 }
1099
1100 popFrontHelper(value);
1101
1102 return e_SUCCESS;
1103}
1104
1105template <class TYPE>
1106int BoundedQueue<TYPE>::pushBack(const TYPE& value)
1107{
1108 int rv = d_pushSemaphore.wait();
1109 if (rv) {
1111 return e_DISABLED; // RETURN
1112 }
1113 return e_FAILED; // RETURN
1114 }
1115
1116 markStartedOperation(&d_pushCount);
1117
1118 // 'd_pushIndex' stores the next location to use (want the original value)
1119
1120 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1121 % d_capacity;
1122 Node& node = d_element_p[index];
1123
1124 node.setIsUnconstructed(true);
1125
1127
1128 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
1129 value,
1130 d_allocator_p);
1131
1132 guard.release();
1133
1134 node.setIsUnconstructed(false);
1135
1136 pushComplete();
1137
1138 return e_SUCCESS;
1139}
1140
1141template <class TYPE>
1143{
1144 int rv = d_pushSemaphore.wait();
1145 if (rv) {
1147 return e_DISABLED; // RETURN
1148 }
1149 return e_FAILED; // RETURN
1150 }
1151
1152 markStartedOperation(&d_pushCount);
1153
1154 // 'd_pushIndex' stores the next location to use (want the original value)
1155
1156 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1157 % d_capacity;
1158 Node& node = d_element_p[index];
1159
1160 node.setIsUnconstructed(true);
1161
1163
1164 TYPE& dummy = value;
1165 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
1166 dummy,
1167 d_allocator_p);
1168
1169 guard.release();
1170
1171 node.setIsUnconstructed(false);
1172
1173 pushComplete();
1174
1175 return e_SUCCESS;
1176}
1177
1178template <class TYPE>
1180{
1181 int reclaim = d_popSemaphore.takeAll();
1182
1183 if (reclaim) {
1184 while (reclaim) {
1185 int count = reclaim;
1186
1187 reclaim = 0;
1188
1189 // For quiescent state detection (see *Implementation* *Note*) and
1190 // eventual 'post' to the 'd_pushSemaphore' to indicate node
1191 // availability, indicate 'count' remove operations have begin.
1192
1193 markStartedOperation(&d_popCount, count);
1194
1195 // 'd_popIndex' stores the next location to use (want the original
1196 // value)
1197
1198 Uint64 index = AtomicOp::addUint64NvAcqRel(&d_popIndex, count)
1199 - count;
1200
1201 for (int i = 0; i < count; ++i, ++index) {
1202 Node& node = d_element_p[index % d_capacity];
1203
1204 if (!node.isUnconstructed()) {
1205 node.d_value.object().~TYPE();
1206 }
1207 else {
1208 ++reclaim;
1209 }
1210 }
1211
1212 // For quiescent state detection (see *Implementation* *Note*) and
1213 // eventual 'post' to the 'd_pushSemaphore' to indicate node
1214 // availability, indicate 'count' remove operations have finished.
1215
1216 Uint64 popCount = markFinishedOperation(&d_popCount, count);
1217
1218 if (isQuiescentState(popCount)) {
1219 // The total number of popped elements is
1220 // 'popCount & k_STARTED_MASK'. Attempt, once, to zero the
1221 // count and, if successful, post to the push semaphore.
1222
1223 if (AtomicOp::testAndSwapUint64AcqRel(&d_popCount,
1224 popCount,
1225 0) == popCount) {
1226 d_pushSemaphore.post(static_cast<int>(
1227 popCount & k_STARTED_MASK));
1228 }
1229
1230 Uint emptyCount = AtomicOp::getUintAcquire(
1231 &d_emptyWaiterCount);
1232
1233 if (isEmpty() && updateEmptyCountSeen(emptyCount)) {
1234 {
1235 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1236 }
1237 d_emptyCondition.broadcast();
1238 }
1239 }
1240 }
1241 }
1242}
1243
1244template <class TYPE>
1245inline
1247{
1248 int rv = d_popSemaphore.tryWait();
1249 if (rv) {
1251 return e_DISABLED; // RETURN
1252 }
1254 return e_EMPTY; // RETURN
1255 }
1256 return e_FAILED; // RETURN
1257 }
1258
1259 popFrontHelper(value);
1260
1261 return e_SUCCESS;
1262}
1263
1264template <class TYPE>
1266{
1267 int rv = d_pushSemaphore.tryWait();
1268 if (rv) {
1270 return e_DISABLED; // RETURN
1271 }
1273 return e_FULL; // RETURN
1274 }
1275 return e_FAILED; // RETURN
1276 }
1277
1278 markStartedOperation(&d_pushCount);
1279
1280 // 'd_pushIndex' stores the next location to use (want the original value)
1281
1282 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1283 % d_capacity;
1284 Node& node = d_element_p[index];
1285
1286 node.setIsUnconstructed(true);
1287
1289
1290 bslalg::ScalarPrimitives::copyConstruct(node.d_value.address(),
1291 value,
1292 d_allocator_p);
1293
1294 guard.release();
1295
1296 node.setIsUnconstructed(false);
1297
1298 pushComplete();
1299
1300 return e_SUCCESS;
1301}
1302
1303template <class TYPE>
1305{
1306 int rv = d_pushSemaphore.tryWait();
1307 if (rv) {
1309 return e_DISABLED; // RETURN
1310 }
1312 return e_FULL; // RETURN
1313 }
1314 return e_FAILED; // RETURN
1315 }
1316
1317 markStartedOperation(&d_pushCount);
1318
1319 // 'd_pushIndex' stores the next location to use (want the original value)
1320
1321 Uint64 index = (AtomicOp::addUint64NvAcqRel(&d_pushIndex, 1) - 1)
1322 % d_capacity;
1323 Node& node = d_element_p[index];
1324
1325 node.setIsUnconstructed(true);
1326
1328
1329 TYPE& dummy = value;
1330 bslalg::ScalarPrimitives::moveConstruct(node.d_value.address(),
1331 dummy,
1332 d_allocator_p);
1333
1334 guard.release();
1335
1336 node.setIsUnconstructed(false);
1337
1338 pushComplete();
1339
1340 return e_SUCCESS;
1341}
1342
1343 // Enqueue/Dequeue State
1344
1345template <class TYPE>
1346inline
1348{
1349 d_popSemaphore.disable();
1350
1351 {
1352 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1353 }
1354 d_emptyCondition.broadcast();
1355}
1356
1357template <class TYPE>
1358inline
1360{
1361 d_pushSemaphore.disable();
1362}
1363
1364template <class TYPE>
1365inline
1367{
1368 d_popSemaphore.enable();
1369}
1370
1371template <class TYPE>
1372inline
1374{
1375 d_pushSemaphore.enable();
1376}
1377
1378// ACCESSORS
1379template <class TYPE>
1380inline
1382{
1383 return static_cast<bsl::size_t>(d_capacity);
1384}
1385
1386template <class TYPE>
1387inline
1389{
1390 return d_capacity == static_cast<Uint64>(d_pushSemaphore.getValue());
1391}
1392
1393template <class TYPE>
1394inline
1396{
1397 return 0 == d_pushSemaphore.getValue();
1398}
1399
1400template <class TYPE>
1401inline
1403{
1404 return d_popSemaphore.isDisabled();
1405}
1406
1407template <class TYPE>
1408inline
1410{
1411 return d_pushSemaphore.isDisabled();
1412}
1413
1414template <class TYPE>
1415inline
1417{
1418 return d_popSemaphore.getValue();
1419}
1420
1421template <class TYPE>
1423{
1424 Uint emptyCount = AtomicOp::addUintNvAcqRel(&d_emptyWaiterCount, 1) - 1;
1425
1426 int state = d_popSemaphore.getDisabledState();
1427 if (1 == (state & 1)) {
1428 return e_DISABLED; // RETURN
1429 }
1430
1431 if (isEmpty()) {
1432 return e_SUCCESS; // RETURN
1433 }
1434
1435 bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
1436
1437 // Return successfully when this queue is empty ('isEmpty()') or this queue
1438 // was empty at some point since this method was invoked (the condition
1439 // tested by 'circularlyGreater' in the below).
1440
1441 bool empty = isEmpty()
1442 || circularlyGreater(AtomicOp::getUintAcquire(&d_emptyCountSeen),
1443 emptyCount);
1444
1445 while (!empty && state == d_popSemaphore.getDisabledState()) {
1446 int rv = d_emptyCondition.wait(&d_emptyMutex);
1447 if (rv) {
1448 return e_FAILED; // RETURN
1449 }
1450 empty = isEmpty()
1451 || circularlyGreater(AtomicOp::getUintAcquire(&d_emptyCountSeen),
1452 emptyCount);
1453 }
1454
1455 if (!empty) {
1456 return e_DISABLED; // RETURN
1457 }
1458
1459 return e_SUCCESS;
1460}
1461
1462 // Aspects
1463
1464template <class TYPE>
1465inline
1467{
1468 return d_allocator_p;
1469}
1470
1471} // close package namespace
1472
1473
1474#endif
1475
1476// ----------------------------------------------------------------------------
1477// Copyright 2019 Bloomberg Finance L.P.
1478//
1479// Licensed under the Apache License, Version 2.0 (the "License");
1480// you may not use this file except in compliance with the License.
1481// You may obtain a copy of the License at
1482//
1483// http://www.apache.org/licenses/LICENSE-2.0
1484//
1485// Unless required by applicable law or agreed to in writing, software
1486// distributed under the License is distributed on an "AS IS" BASIS,
1487// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1488// See the License for the specific language governing permissions and
1489// limitations under the License.
1490// ----------------------------- END-OF-FILE ----------------------------------
1491
1492/** @} */
1493/** @} */
1494/** @} */
#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:779
Definition bdlcc_boundedqueue.h:313
void release()
Definition bdlcc_boundedqueue.h:810
~BoundedQueue_PushExceptionCompleteProctor()
Definition bdlcc_boundedqueue.h:800
Definition bdlcc_boundedqueue.h:415
bsl::size_t capacity() const
Definition bdlcc_boundedqueue.h:1381
bool isPopFrontDisabled() const
Definition bdlcc_boundedqueue.h:1402
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_boundedqueue.h:1142
int waitUntilEmpty() const
Definition bdlcc_boundedqueue.h:1422
void enablePushBack()
Definition bdlcc_boundedqueue.h:1373
BoundedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_boundedqueue.h:1049
void removeAll()
Definition bdlcc_boundedqueue.h:1179
void enablePopFront()
Definition bdlcc_boundedqueue.h:1366
int tryPopFront(TYPE *value)
Definition bdlcc_boundedqueue.h:1246
int popFront(TYPE *value)
Definition bdlcc_boundedqueue.h:1090
bool isEmpty() const
Definition bdlcc_boundedqueue.h:1388
void disablePopFront()
Definition bdlcc_boundedqueue.h:1347
int pushBack(const TYPE &value)
Definition bdlcc_boundedqueue.h:1106
bsl::size_t numElements() const
Definition bdlcc_boundedqueue.h:1416
bool isFull() const
Definition bdlcc_boundedqueue.h:1395
bool isPushBackDisabled() const
Definition bdlcc_boundedqueue.h:1409
~BoundedQueue()
Destroy this object.
Definition bdlcc_boundedqueue.h:1079
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_boundedqueue.h:1466
int tryPushBack(const TYPE &value)
Definition bdlcc_boundedqueue.h:1265
void disablePushBack()
Definition bdlcc_boundedqueue.h:1359
TYPE value_type
Definition bdlcc_boundedqueue.h:611
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_boundedqueue.h:1304
Definition bslma_allocator.h:457
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:751
Definition bslmt_condition.h:220
Definition bslmt_fastpostsemaphore.h:327
void post()
Atomically increment the count of this semaphore.
Definition bslmt_fastpostsemaphore.h:604
@ e_DISABLED
Definition bslmt_fastpostsemaphore.h:347
@ e_WOULD_BLOCK
Definition bslmt_fastpostsemaphore.h:352
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
bsls::ObjectBuffer< TYPE > d_value
Definition bdlcc_boundedqueue.h:394
bsls::ObjectBuffer< TYPE > d_value
Definition bdlcc_boundedqueue.h:373
Definition bdlcc_boundedqueue.h:363
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
Definition bslmf_isbitwisecopyable.h:298
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1060
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