BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_singleconsumerqueueimpl.h
Go to the documentation of this file.
1/// @file bdlcc_singleconsumerqueueimpl.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_singleconsumerqueueimpl.h -*-C++-*-
8
9#ifndef INCLUDED_BDLCC_SINGLECONSUMERQUEUEIMPL
10#define INCLUDED_BDLCC_SINGLECONSUMERQUEUEIMPL
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bdlcc_singleconsumerqueueimpl bdlcc_singleconsumerqueueimpl
16/// @brief Provide a testable thread-aware single consumer queue of values.
17/// @addtogroup bdl
18/// @{
19/// @addtogroup bdlcc
20/// @{
21/// @addtogroup bdlcc_singleconsumerqueueimpl
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bdlcc_singleconsumerqueueimpl-purpose"> Purpose</a>
26/// * <a href="#bdlcc_singleconsumerqueueimpl-classes"> Classes </a>
27/// * <a href="#bdlcc_singleconsumerqueueimpl-description"> Description </a>
28/// * <a href="#bdlcc_singleconsumerqueueimpl-allocator-requirements"> Allocator Requirements </a>
29/// * <a href="#bdlcc_singleconsumerqueueimpl-exception-safety"> Exception Safety </a>
30/// * <a href="#bdlcc_singleconsumerqueueimpl-move-semantics-in-c-03"> Move Semantics in C++03 </a>
31/// * <a href="#bdlcc_singleconsumerqueueimpl-memory-usage"> Memory Usage </a>
32/// * <a href="#bdlcc_singleconsumerqueueimpl-warning-synchronization-required-on-destruction"> WARNING: Synchronization Required on Destruction </a>
33/// * <a href="#bdlcc_singleconsumerqueueimpl-usage"> Usage </a>
34///
35/// # Purpose {#bdlcc_singleconsumerqueueimpl-purpose}
36/// Provide a testable thread-aware single consumer queue of values.
37///
38/// # Classes {#bdlcc_singleconsumerqueueimpl-classes}
39///
40/// - bdlcc::SingleConsumerQueueImpl: thread-aware single consumer `TYPE` queue
41///
42/// # Description {#bdlcc_singleconsumerqueueimpl-description}
43/// This component defines a type,
44/// `bdlcc::SingleConsumerQueueImpl`, that provides an efficient, thread-aware
45/// queue of values assuming a single consumer (the use of `popFront`,
46/// `tryPopFront`, and `removeAll` is done by one thread or a group of threads
47/// using external synchronization). The behavior of the methods `popFront`,
48/// `tryPopFront`, and `removeAll` is undefined unless the use is by a single
49/// consumer. This class is ideal for synchronization and communication between
50/// threads in a producer-consumer model when there is only one consumer thread.
51///
52/// The queue provides `pushBack` and `popFront` methods for pushing data into
53/// the queue and popping data from the queue. The queue will allocate memory
54/// as necessary to accommodate `pushBack` invocations (`pushBack` will never
55/// block and is provided for consistency with other containers). 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 `tryPopFront` method fails immediately, returning a non-zero
59/// value, if the queue is empty.
60///
61/// The queue may be placed into a "enqueue disabled" state using the
62/// `disablePushBack` method. When disabled, `pushBack` and `tryPushBack` fail
63/// immediately and return an error code. The queue may be restored to normal
64/// operation with the `enablePushBack` method.
65///
66/// The queue may be placed into a "dequeue disabled" state using the
67/// `disablePopFront` method. When dequeue disabled, `popFront` and
68/// `tryPopFront` fail immediately and return an error code. Any threads
69/// blocked in `popFront` when the queue is dequeue disabled return from
70/// `popFront` immediately and return an error code.
71///
72/// ## Allocator Requirements {#bdlcc_singleconsumerqueueimpl-allocator-requirements}
73///
74///
75/// Access to the allocator supplied to the constructor is internally
76/// synchronized by this component. If allocations performed by this component
77/// must be synchronized with external allocations (performed outside of this
78/// component), that synchronization must be guaranteed by the user. Using a
79/// thread-safe allocator is the common way to satisfy this requirement.
80///
81/// ## Exception Safety {#bdlcc_singleconsumerqueueimpl-exception-safety}
82///
83///
84/// A `bdlcc::SingleConsumerQueueImpl` is exception neutral, and all of the
85/// methods of `bdlcc::SingleConsumerQueueImpl` provide the basic exception
86/// safety guarantee (see @ref bsldoc_glossary ).
87///
88/// ## Move Semantics in C++03 {#bdlcc_singleconsumerqueueimpl-move-semantics-in-c-03}
89///
90///
91/// Move-only types are supported by `bdlcc::SingleConsumerQueueImpl` on C++11
92/// platforms only (where `BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES` is defined),
93/// and are not supported on C++03 platforms. Unfortunately, in C++03, there
94/// are user types where a `bslmf::MovableRef` will not safely degrade to a
95/// lvalue reference when a move constructor is not available (types providing a
96/// constructor template taking any type), so `bslmf::MovableRefUtil::move`
97/// cannot be used directly on a user supplied template type. See internal bug
98/// report 99039150 for more information.
99///
100/// ## Memory Usage {#bdlcc_singleconsumerqueueimpl-memory-usage}
101///
102///
103/// `bdlcc::SingleConsumerQueueImpl` is most efficient when dealing with small
104/// objects or fundamental types (as a thread-safe container, its methods pass
105/// objects *by* *value*). We recommend large objects be stored as
106/// shared-pointers (or possibly raw pointers).
107///
108/// ## WARNING: Synchronization Required on Destruction {#bdlcc_singleconsumerqueueimpl-warning-synchronization-required-on-destruction}
109///
110///
111/// The behavior for the destructor is undefined unless all access or
112/// modification of the object is completed prior to its destruction. Some form
113/// of synchronization, external to the component, is required to ensure the
114/// precondition on the destructor is met. For example, if two (or more)
115/// threads are manipulating a queue, it is *not* safe to anticipate the number
116/// of elements added to the queue, and destroy that queue immediately after the
117/// last element is popped (without additional synchronization) because one of
118/// the corresponding push functions may not have completed (push may, for
119/// instance, signal waiting threads after the element is considered added to
120/// the container).
121///
122/// ## Usage {#bdlcc_singleconsumerqueueimpl-usage}
123///
124///
125/// There is no usage example for this component since it is not meant for
126/// direct client use.
127/// @}
128/** @} */
129/** @} */
130
131/** @addtogroup bdl
132 * @{
133 */
134/** @addtogroup bdlcc
135 * @{
136 */
137/** @addtogroup bdlcc_singleconsumerqueueimpl
138 * @{
139 */
140
141#include <bdlscm_version.h>
142
144
146
148
149#include <bslmf_movableref.h>
151
152#include <bslmt_lockguard.h>
153#include <bslmt_threadutil.h>
154
155#include <bsls_assert.h>
156#include <bsls_objectbuffer.h>
157#include <bsls_types.h>
158
159#include <bsl_cstddef.h>
160
161
162namespace bdlcc {
163
164 // ================================================
165 // class SingleConsumerQueueImpl_MarkReclaimProctor
166 // ================================================
167
168/// This class implements a proctor that, unless its `release` method has
169/// previously been invoked, automatically invokes `markReclaim` on a `NODE`
170/// upon destruction.
171///
172/// See @ref bdlcc_singleconsumerqueueimpl
173template <class TYPE, class NODE>
175
176 // DATA
177 TYPE *d_queue_p; // managed queue owning the managed node
178 NODE *d_node_p; // managed node
179
180 private:
181 // NOT IMPLEMENTED
187
188 public:
189 // CREATORS
190
191 /// Create a `markReclaim` proctor managing the specified `node` of the
192 /// specified `queue`.
193 SingleConsumerQueueImpl_MarkReclaimProctor(TYPE *queue, NODE *node);
194
195 /// Destroy this object and, if `release` has not been invoked, invoke
196 /// the managed queue's `markReclaim` method with the managed node.
198
199 // MANIPULATORS
200
201 /// Release from management the queue and node currently managed by this
202 /// proctor. If no queue, this method has no effect.
203 void release();
204};
205
206 // ==============================================
207 // class SingleConsumerQueueImpl_PopCompleteGuard
208 // ==============================================
209
210/// This class implements a guard that automatically invokes `popComplete`
211/// on the managed queue upon destruction.
212///
213/// See @ref bdlcc_singleconsumerqueueimpl
214template <class TYPE>
216
217 // DATA
218 TYPE *d_queue_p; // managed queue
219
220 private:
221 // NOT IMPLEMENTED
227
228 public:
229 // CREATORS
230
231 /// Create a `popComplete` guard managing the specified `queue`.
232 explicit
234
235 /// Destroy this object and invoke the `popComplete` method on the
236 /// managed queue.
238};
239
240 // ===============================================
241 // class SingleConsumerQueueImpl_AllocateLockGuard
242 // ===============================================
243
244/// This class implements a guard that automatically invokes
245/// `releaseAllocateLock` on the managed queue upon destruction.
246///
247/// See @ref bdlcc_singleconsumerqueueimpl
248template <class TYPE>
250
251 // DATA
252 TYPE *d_queue_p; // managed queue
253
254 private:
255 // NOT IMPLEMENTED
261
262 public:
263 // CREATORS
264
265 /// Create a `releaseAllocateLock` guard managing the specified `queue`.
266 explicit SingleConsumerQueueImpl_AllocateLockGuard(TYPE *queue);
267
268 /// Destroy this object and invoke the managed queue's
270 // 'releaseAllocateLock' method.
271};
272
273 // =============================
274 // class SingleConsumerQueueImpl
275 // =============================
276
277/// This class provides a thread-safe unbounded queue of values that assumes
278/// a single consumer thread.
279///
280/// The types `ATOMIC_OP`, `MUTEX`, and `CONDITION` are exposed for testing.
281/// Typical usage is with `bsls::AtomicOperations` for `ATOMIC_OP`,
282/// `bslmt::Mutex` for `MUTEX`, and `bslmt::Condition` for `CONDITION`.
283///
284/// See @ref bdlcc_singleconsumerqueueimpl
285template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
287
288 // PRIVATE CONSTANTS
289 enum {
290 // These value are used as values for 'd_state' in 'Node'. A node is
291 // writable at creation and after a read completes (when the producers
292 // can write to the node). A node is readable after it is written
293 // (when the node can be read by the single consumer). The states
294 // in-between these two states (e.g., writing) are not needed by this
295 // implementation of the queue.
296
297 e_READABLE, // node can be read
298 e_WRITABLE, // node can be written
299 e_WRITABLE_AND_BLOCKED, // node can be written and has blocked reader
300 e_RECLAIM // node suffered exception while being written
301 };
302
303 static const bsl::size_t k_ALLOCATION_BATCH_SIZE = 8;
304 // number of nodes to allocate during a
305 // 'pushBack' when no nodes are available for
306 // reuse
307
308 // The queus's state is maintained in 'd_state' whose bits have the
309 // following meaning (and can be maintained by the constants below):
310 //..
311 // 63 19 18 0
312 // +-----------+--+-----+
313 // | available |a | use |
314 // +-----------+--+-----+
315 //..
316 //
317 //: * available: the number of nodes available for use, which becomes
318 //: negative when an allocation is needed
319 //:
320 //: * a (allocate): a bit indicating a thread is holding the allocation
321 //: lock
322 //:
323 //: * use: number of threads attempting to use existing nodes
324 //
325 // The 'k_*_MASK' constants define the layout of the attributes, the
326 // 'k_*_INC' constants are used to modify the 'd_state' attributes, and the
327 // 'k_*_SHIFT' constants allow recovery of the stored value.
328 //
329 // See *Implementation* *Note* for further details.
330
331 static const bsls::Types::Int64 k_ALLOCATE_MASK = 0x0000000000080000LL;
332 static const bsls::Types::Int64 k_ALLOCATE_INC = 0x0000000000080000LL;
333 static const bsls::Types::Int64 k_USE_MASK = 0x000000000007ffffLL;
334 static const bsls::Types::Int64 k_USE_INC = 0x0000000000000001LL;
335 static const bsls::Types::Int64 k_AVAILABLE_MASK = 0xfffffffffff00000LL;
336 static const bsls::Types::Int64 k_AVAILABLE_INC = 0x0000000000100000LL;
337 static const int k_AVAILABLE_SHIFT = 20;
338
339 // PRIVATE TYPES
340 typedef typename ATOMIC_OP::AtomicTypes::Int AtomicInt;
341 typedef typename ATOMIC_OP::AtomicTypes::Int64 AtomicInt64;
342 typedef typename ATOMIC_OP::AtomicTypes::Uint AtomicUint;
343 typedef typename ATOMIC_OP::AtomicTypes::Pointer AtomicPointer;
344
345 template <class T>
346 struct QueueNode {
347 // PUBLIC DATA
348 bsls::ObjectBuffer<T> d_value; // stored value
349 AtomicInt d_state; // 'e_READABLE', 'e_WRITABLE', etc.
350 AtomicPointer d_next; // pointer to next node
351 };
352
353 typedef QueueNode<TYPE> Node;
355
356 // DATA
357 AtomicPointer d_nextWrite; // pointer to next write to node
358
359 AtomicPointer d_nextRead; // pointer to next read from node
360
361 MUTEX d_readMutex; // used with 'd_readCondition' to
362 // block until an element is
363 // available for popping
364
365 CONDITION d_readCondition; // condition variable for popping
366 // thread
367
368 MUTEX d_writeMutex; // during allocation, used to
369 // synchronize threads access to
370 // 'd_nextWrite'
371
372 AtomicInt64 d_capacity; // capacity of this queue
373
374 mutable MUTEX d_emptyMutex; // blocking point for consumer
375 // during 'waitUntilEmpty'
376
377 mutable CONDITION d_emptyCondition; // condition variable for consumer
378 // during 'waitUntilEmpty'
379
380 AtomicInt64 d_state; // bit pattern representing the
381 // state of the queue (see
382 // implementation notes)
383
384 AtomicUint d_popFrontDisabled; // is queue pop disabled and
385 // generation count; see
386 // *Implementation* *Note*
387
388 AtomicUint d_pushBackDisabled; // is queue push disabled and
389 // generation count; see
390 // *Implementation* *Note*
391
392 Allocator d_allocator; // allocator
393
394 // FRIENDS
397 ATOMIC_OP,
398 MUTEX,
399 CONDITION>,
400 typename SingleConsumerQueueImpl<TYPE,
401 ATOMIC_OP,
402 MUTEX,
403 CONDITION>::Node >;
404
406 SingleConsumerQueueImpl<TYPE,
407 ATOMIC_OP,
408 MUTEX,
409 CONDITION> >;
410
412 SingleConsumerQueueImpl<TYPE,
413 ATOMIC_OP,
414 MUTEX,
415 CONDITION> >;
416
417 // PRIVATE CLASS METHODS
418
419 /// Return the available attribute from the specified `state`.
420 static bsls::Types::Int64 available(bsls::Types::Int64 state);
421
422 // PRIVATE MANIPULATORS
423
424 /// If the specified `value` does not have its lowest-order bit set to
425 /// the value of the specified `bitValue`, increment `value` until it does.
426 ///
427 /// \note Note that this method is used to modify the generation counts
428 /// stored in `d_popFrontDisabled` and `d_pushBackDisabled`. See
429 /// *Implementation* *Note* for further details.
430 void incrementUntil(AtomicUint *value, unsigned int bitValue);
431
432 /// Mark the specified `node` as a node to be reclaimed.
433 void markReclaim(Node *node);
434
435 /// If the specified `destruct` is true, destruct the value stored in
436 /// `d_nextRead`. Mark `d_nextRead` writable, and if the queue is empty
437 /// then signal the queue empty condition. This method is used to
438 /// complete the reclamation of a node in the presence of an exception.
439 void popComplete(bool destruct);
440
441 /// Return a pointer to the node to assign the value being pushed into
442 /// this queue, or 0 if `isPushBackDisabled()`.
443 Node *pushBackHelper();
444
445 /// Remove the allocation lock indicator from `d_state`. This method is
446 /// intended to be used to remove the allocation lock indicator from
447 /// `d_state` when there is an exception during allocation and the
448 /// locked state is set (i.e., `pushBackHelper`).
449 void releaseAllocateLock();
450
451 private:
452 // NOT IMPLEMENTED
453 SingleConsumerQueueImpl(const SingleConsumerQueueImpl&);
454 SingleConsumerQueueImpl& operator=(const SingleConsumerQueueImpl&);
455
456 public:
457 // TRAITS
458 BSLMF_NESTED_TRAIT_DECLARATION(SingleConsumerQueueImpl,
459 bslma::UsesBslmaAllocator);
460
461 // PUBLIC TYPES
462 typedef TYPE value_type; // The type for elements.
463
464 // PUBLIC CONSTANTS
465 enum {
466 e_SUCCESS = 0, // must be 0
467 e_EMPTY = -1,
468 e_DISABLED = -2
469 };
470
471 // CREATORS
472
473 /// Create a thread-aware queue. Optionally specify a `basicAllocator`
474 /// used to supply memory. If `basicAllocator` is 0, the currently
475 /// installed default allocator is used.
476 explicit
478
479 /// Create a thread-aware queue with, at least, the specified
480 /// `capacity`. Optionally specify a `basicAllocator` used to supply
481 /// memory. If `basicAllocator` is 0, the currently installed default
482 /// allocator is used.
483 explicit
484 SingleConsumerQueueImpl(bsl::size_t capacity,
485 bslma::Allocator *basicAllocator = 0);
486
487 /// Destroy this container.
488 /// \pre The behavior is undefined unless all access
489 /// or modification of the container has completed prior to this call.
491
492 // MANIPULATORS
493
494 /// Remove the element from the front of this queue and load that
495 /// element into the specified `value`. If the queue is empty, block
496 /// until it is not empty. Return 0 on success, and a non-zero value
497 /// otherwise. Specifically, return `e_DISABLED` if
498 /// `isPopFrontDisabled()`. On failure, `value` is not changed.
499 /// Threads blocked due to the queue being empty will return
500 /// `e_DISABLED` if `disablePopFront` is invoked.
501 ///
502 /// \pre The behavior is undefined unless the invoker of this method is the single consumer.
503 int popFront(TYPE *value);
504
505 /// Append the specified `value` to the back of this queue. Return 0 on
506 /// success, and a non-zero value otherwise. Specifically, return
507 /// `e_DISABLED` if `isPushBackDisabled()`.
508 int pushBack(const TYPE& value);
509
510 /// Append the specified move-insertable `value` to the back of this
511 /// queue. `value` is left in a valid but unspecified state. Return 0
512 /// on success, and a non-zero value otherwise. Specifically, return
513 /// `e_DISABLED` if `isPushBackDisabled()`. On failure, `value` is not
514 /// changed.
516
517 /// Remove all items currently in this queue.
518 /// \note Note that this operation
519 /// is not atomic; if other threads are concurrently pushing items into
520 /// the queue the result of `numElements()` after this function returns is not guaranteed to be 0.
521 ///
522 /// \pre The behavior is undefined unless the
523 /// invoker of this method is the single consumer.
524 void removeAll();
525
526 /// Attempt to remove the element from the front of this queue without
527 /// blocking, and, if successful, load the specified `value` with the
528 /// removed element. Return 0 on success, and a non-zero value
529 /// otherwise. Specifically, return `e_DISABLED` if
530 /// `isPopFrontDisabled()`, and `e_EMPTY` if `!isPopFrontDisabled()` and
531 /// the queue was empty. On failure, `value` is not changed.
532 ///
533 /// \pre The behavior is undefined unless the invoker of this method is the
534 /// single consumer.
535 int tryPopFront(TYPE *value);
536
537 /// Append the specified `value` to the back of this queue. Return 0 on
538 /// success, and a non-zero value otherwise. Specifically, retun
539 /// `e_DISABLED` if `isPushBackDisabled()`.
540 int tryPushBack(const TYPE& value);
541
542 /// Append the specified move-insertable `value` to the back of this
543 /// queue. `value` is left in a valid but unspecified state. Return 0
544 /// on success, and a non-zero value otherwise. Specifically, return
545 /// `e_DISABLED` if `isPushBackDisabled()`. On failure, `value` is not
546 /// changed.
548
549 // Enqueue/Dequeue State
550
551 /// Disable dequeueing from this queue. All subsequent invocations of
552 /// `popFront` or `tryPopFront` will fail immediately. All blocked
553 /// invocations of `popFront` and `waitUntilEmpty` will fail
554 /// immediately. If the queue is already dequeue disabled, this method
555 /// has no effect.
557
558 /// Disable enqueueing into this queue. All subsequent invocations of
559 /// `pushBack` or `tryPushBack` will fail immediately. All blocked
560 /// invocations of `pushBack` will fail immediately. If the queue is
561 /// already enqueue disabled, this method has no effect.
563
564 /// Enable dequeueing. If the queue is not dequeue disabled, this call
565 /// has no effect.
567
568 /// Enable queuing. If the queue is not enqueue disabled, this call has
569 /// no effect.
571
572 // ACCESSORS
573
574 /// Return `true` if this queue is empty (has no elements), or `false`
575 /// otherwise.
576 bool isEmpty() const;
577
578 /// Return `true` if this queue is full (has no available capacity), or `false` otherwise.
579 ///
580 /// \note Note that for unbounded queues, this method
581 /// always returns `false`.
582 bool isFull() const;
583
584 /// Return `true` if this queue is dequeue disabled, and `false` otherwise.
585 ///
586 /// \note Note that the queue is created in the "dequeue enabled"
587 /// state.
588 bool isPopFrontDisabled() const;
589
590 /// Return `true` if this queue is enqueue disabled, and `false` otherwise.
591 ///
592 /// \note Note that the queue is created in the "enqueue enabled"
593 /// state.
594 bool isPushBackDisabled() const;
595
596 /// Returns the number of elements currently in this queue.
597 bsl::size_t numElements() const;
598
599 /// Block until all the elements in this queue are removed. Return 0 on
600 /// success, and a non-zero value otherwise. Specifically, return
601 /// `e_DISABLED` if `!isEmpty() && isPopFrontDisabled()`. A blocked
602 /// thread waiting for the queue to empty will return `e_DISABLED` if
603 /// `disablePopFront` is invoked.
604 int waitUntilEmpty() const;
605
606 // Aspects
607
608 /// Return the allocator used by this object to supply memory.
610};
611
612// ============================================================================
613// INLINE DEFINITIONS
614// ============================================================================
615
616 // ------------------------------------------------
617 // class SingleConsumerQueueImpl_MarkReclaimProctor
618 // ------------------------------------------------
619
620// CREATORS
621template <class TYPE, class NODE>
623 SingleConsumerQueueImpl_MarkReclaimProctor(TYPE *queue, NODE *node)
624: d_queue_p(queue)
625, d_node_p(node)
626{
627}
628
629template <class TYPE, class NODE>
632{
633 if (d_queue_p) {
634 d_queue_p->markReclaim(d_node_p);
635 }
636}
637
638// MANIPULATORS
639template <class TYPE, class NODE>
644
645 // ----------------------------------------------
646 // class SingleConsumerQueueImpl_PopCompleteGuard
647 // ----------------------------------------------
648
649// CREATORS
650template <class TYPE>
656
657template <class TYPE>
663
664 // ------------------------------------------------------
665 // class SingleConsumerQueueImpl_AllocateLockGuardProctor
666 // ------------------------------------------------------
667
668// CREATORS
669template <class TYPE>
675
676template <class TYPE>
682
683 // -----------------------------
684 // class SingleConsumerQueueImpl
685 // -----------------------------
686
687// PRIVATE CLASS METHODS
688template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
691{
692 return state >> k_AVAILABLE_SHIFT;
693}
694
695// PRIVATE MANIPULATORS
696template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
697void SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
698 ::incrementUntil(AtomicUint *value, unsigned int bitValue)
699{
700 unsigned int state = ATOMIC_OP::getUintAcquire(value);
701 if (bitValue != (state & 1)) {
702 unsigned int expState;
703 do {
704 expState = state;
705 state = ATOMIC_OP::testAndSwapUintAcqRel(value,
706 state,
707 state + 1);
708 } while (state != expState && (bitValue != (state & 1)));
709 }
710}
711
712template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
713void SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
714 ::markReclaim(Node *node)
715{
716 // A reclaimed node is used to denote an exception occurred and the node is
717 // currently invalid. A reclaimed node is considered removed and not
718 // counted as part of the capacity of the queue (thus ensuring
719 // 'numElements' is correct).
720
721 ATOMIC_OP::addInt64AcqRel(&d_capacity, -1);
722
723 int nodeState = ATOMIC_OP::swapIntAcqRel(&node->d_state, e_RECLAIM);
724 if (e_WRITABLE_AND_BLOCKED == nodeState) {
725 {
726 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
727 }
728 d_readCondition.signal();
729 }
730}
731
732template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
733void SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
734 ::popComplete(bool destruct)
735{
736 Node *nextRead =
737 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
738
739 if (destruct) {
740 nextRead->d_value.object().~TYPE();
741 }
742
743 ATOMIC_OP::setIntRelease(&nextRead->d_state, e_WRITABLE);
744
745 ATOMIC_OP::setPtrRelease(&d_nextRead,
746 ATOMIC_OP::getPtrAcquire(&nextRead->d_next));
747
748 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
749 k_AVAILABLE_INC);
750
751 if (ATOMIC_OP::getInt64Acquire(&d_capacity) == available(state)) {
752 {
753 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
754 }
755 d_emptyCondition.broadcast();
756 }
757}
758
759template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
760typename SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::Node *
761 SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
762 ::pushBackHelper()
763{
764 if (1 == (ATOMIC_OP::getUintAcquire(&d_pushBackDisabled) & 1)) {
765 return 0; // RETURN
766 }
767
768 // Fast path requires an available node ('-k_AVAILABLE_INC') and needs to
769 // indicate a thread is intending to use an existing node ('k_USE_INC').
770
771 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(
772 &d_state,
773 k_USE_INC - k_AVAILABLE_INC);
774
775 if (state < 0 || 0 != (state & k_ALLOCATE_MASK)) {
776 // The determination to use an existing node was premature, undo the
777 // indication.
778
779 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
780 k_AVAILABLE_INC - k_USE_INC);
781
782 bsls::Types::Int64 expState;
783
784 do {
785 expState = state;
786 if (state >= k_AVAILABLE_INC && 0 == (state & k_ALLOCATE_MASK)) {
787 // The are now sufficient available nodes to reserve one. This
788 // can be due to an allocation completing or a node being made
789 // available by the consumer.
790
791 state = ATOMIC_OP::testAndSwapInt64AcqRel(
792 &d_state,
793 state,
794 state + k_USE_INC - k_AVAILABLE_INC);
795 }
796 else if ( 0 == ( (state >> k_AVAILABLE_SHIFT)
797 + (state & k_USE_MASK))
798 && 0 == (state & k_ALLOCATE_MASK)) {
799 // '-AVAILABLE == USE' indicates all threads will wait for the
800 // allocation, so attempt to become the allocating thread.
801 // Note that 'AVAILABLE < 0 && -AVAILABLE != USE' indicates the
802 // temporary state where threads are still completing the
803 // re-use of an existing node or there are available nodes to
804 // be re-used (nodes were allocated or made available by the
805 // consumer). If threads are still completing the re-use of an
806 // existing node, ownership of 'd_nextWrite' can not be
807 // guaranteed by setting the allocation lock.
808
809 state = ATOMIC_OP::testAndSwapInt64AcqRel(
810 &d_state,
811 state,
812 state + k_ALLOCATE_INC);
813 if (expState == state) {
814 // This thread is the only thread acccessing 'd_nextWrite'.
815 // Allocate new nodes and insert them. The variables 'a'
816 // and 'b' in the below are pointers to 'Node' as per the
817 // following diagram.
818 //..
819 // d_nextWrite
820 // |
821 // V
822 // +---+ +---+
823 // --> | | --> | | -->
824 // +---+ +---+
825 // ^ ^
826 // | |
827 // a b
828 //..
829
830 SingleConsumerQueueImpl_AllocateLockGuard<
831 SingleConsumerQueueImpl<TYPE,
832 ATOMIC_OP,
833 MUTEX,
834 CONDITION> > guard(this);
835
836 Node *a = static_cast<Node *>(
837 ATOMIC_OP::getPtrAcquire(&d_nextWrite));
838
839 Node *b = static_cast<Node *>(
840 ATOMIC_OP::getPtrAcquire(&a->d_next));
841
842 Node *nodes = static_cast<Node *>(
843 d_allocator.allocate( sizeof(Node)
844 * k_ALLOCATION_BATCH_SIZE));
845 for (bsl::size_t i = 0;
846 i < k_ALLOCATION_BATCH_SIZE - 1;
847 ++i) {
848 Node *n = nodes + i;
849 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
850 ATOMIC_OP::initPointer(&n->d_next, n + 1);
851 }
852 {
853 Node *n = nodes + k_ALLOCATION_BATCH_SIZE - 1;
854 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
855 ATOMIC_OP::initPointer(&n->d_next, b);
856 }
857
858 ATOMIC_OP::setPtrRelease(&a->d_next, nodes);
859 ATOMIC_OP::setPtrRelease(&d_nextWrite, nodes);
860
861 ATOMIC_OP::addInt64AcqRel(&d_capacity,
862 k_ALLOCATION_BATCH_SIZE);
863
864 // Reserve one node for this thread and make the other
865 // nodes available to other threads.
866
867 ATOMIC_OP::addInt64AcqRel(
868 &d_state,
869 k_AVAILABLE_INC * (k_ALLOCATION_BATCH_SIZE - 1));
870
871 return a; // RETURN
872 }
873
874 expState = ~state; // cause the 'while' to fail and remain in
875 // this loop
876 }
877 else {
879 state = ATOMIC_OP::getInt64Acquire(&d_state);
880
881 expState = ~state; // cause the 'while' to fail and remain in
882 // this loop
883 }
884 } while (state != expState);
885 }
886
887 Node *nextWrite =
888 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextWrite));
889
890 Node *expNextWrite;
891 do {
892 expNextWrite = nextWrite;
893 Node *next = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(
894 &nextWrite->d_next));
895
896 nextWrite = static_cast<Node *>(ATOMIC_OP::testAndSwapPtrAcqRel(
897 &d_nextWrite,
898 nextWrite,
899 next));
900 } while (nextWrite != expNextWrite);
901
902 ATOMIC_OP::addInt64AcqRel(&d_state, -k_USE_INC);
903
904 return nextWrite;
905}
906
907template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
908inline
909void SingleConsumerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
910 ::releaseAllocateLock()
911{
912 ATOMIC_OP::addInt64AcqRel(&d_state, -k_ALLOCATE_INC);
913}
914
915// CREATORS
916template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
919: d_readMutex()
920, d_readCondition()
921, d_writeMutex()
922, d_emptyMutex()
923, d_emptyCondition()
924, d_allocator(basicAllocator)
925{
926 ATOMIC_OP::initInt64(&d_capacity, 0);
927 ATOMIC_OP::initInt64(&d_state, 0);
928
929 ATOMIC_OP::initUint(&d_popFrontDisabled, 0);
930 ATOMIC_OP::initUint(&d_pushBackDisabled, 0);
931
932 ATOMIC_OP::initPointer(&d_nextWrite, 0);
933
934 Node *n = static_cast<Node *>(d_allocator.allocate(sizeof(Node)));
935 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
936 ATOMIC_OP::initPointer(&n->d_next, n);
937
938 ATOMIC_OP::setPtrRelease(&d_nextWrite, n);
939 ATOMIC_OP::setPtrRelease(&d_nextRead, n);
940}
941
942template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
944 SingleConsumerQueueImpl(bsl::size_t capacity,
945 bslma::Allocator *basicAllocator)
946: d_readMutex()
947, d_readCondition()
948, d_writeMutex()
949, d_emptyMutex()
950, d_emptyCondition()
951, d_allocator(basicAllocator)
952{
953 ATOMIC_OP::initInt64(&d_capacity, 0);
954 ATOMIC_OP::initInt64(&d_state, 0);
955
956 ATOMIC_OP::initUint(&d_popFrontDisabled, 0);
957 ATOMIC_OP::initUint(&d_pushBackDisabled, 0);
958
959 ATOMIC_OP::initPointer(&d_nextWrite, 0);
960
961 Node *nodes = static_cast<Node *>(d_allocator.allocate( sizeof(Node)
962 * (capacity + 1)));
963 for (bsl::size_t i = 0; i < capacity; ++i) {
964 Node *n = nodes + i;
965 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
966 ATOMIC_OP::initPointer(&n->d_next, n + 1);
967 }
968 {
969 Node *n = nodes + capacity;
970 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
971 ATOMIC_OP::initPointer(&n->d_next, nodes);
972 }
973
974 ATOMIC_OP::setPtrRelease(&d_nextWrite, nodes);
975 ATOMIC_OP::setPtrRelease(&d_nextRead, nodes);
976
977 ATOMIC_OP::addInt64AcqRel(&d_capacity, capacity);
978 ATOMIC_OP::addInt64AcqRel(&d_state, k_AVAILABLE_INC * capacity);
979}
980
981template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
984{
985 Node *end = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextWrite));
986
987 if (end) {
988 Node *at = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&end->d_next));
989
990 while (at != end) {
991 Node *next =
992 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&at->d_next));
993
994 if (e_READABLE == ATOMIC_OP::getIntAcquire(&at->d_state)) {
995 at->d_value.object().~TYPE();
996 }
997
998 at = next;
999 }
1000
1001 if (e_READABLE == ATOMIC_OP::getIntAcquire(&at->d_state)) {
1002 at->d_value.object().~TYPE();
1003 }
1004 }
1005}
1006
1007// MANIPULATORS
1008template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1010 TYPE *value)
1011{
1012 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
1013 if (1 == (generation & 1)) {
1014 return e_DISABLED; // RETURN
1015 }
1016
1017 Node *nextRead =
1018 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1019 int nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1020 do {
1021 // Note that 'e_WRITABLE_AND_BLOCKED != nodeState' since if the one
1022 // consumer sets this state, the one consumer waits until the node is
1023 // readable, and either the producer that signalled the consumer
1024 // changed the node state already, or the consumer will change the node
1025 // state in 'popComplete'.
1026
1027 if (e_WRITABLE == nodeState) {
1029 nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1030 if (e_WRITABLE == nodeState) {
1031 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1032 nodeState = ATOMIC_OP::swapIntAcqRel(&nextRead->d_state,
1033 e_WRITABLE_AND_BLOCKED);
1034 while (e_READABLE != nodeState && e_RECLAIM != nodeState) {
1035 if (generation !=
1036 ATOMIC_OP::getUintAcquire(&d_popFrontDisabled)) {
1037 ATOMIC_OP::testAndSwapIntAcqRel(&nextRead->d_state,
1038 e_WRITABLE_AND_BLOCKED,
1039 e_WRITABLE);
1040 return e_DISABLED; // RETURN
1041 }
1042 d_readCondition.wait(&d_readMutex);
1043 nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1044 }
1045 }
1046 }
1047 if (e_RECLAIM == nodeState) {
1048 ATOMIC_OP::addInt64AcqRel(&d_capacity, 1);
1049 popComplete(false);
1050 nextRead =
1051 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1052 nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1053 }
1054 } while (e_RECLAIM == nodeState);
1055
1058 ATOMIC_OP,
1059 MUTEX,
1060 CONDITION> > guard(this);
1061
1062#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
1063 *value = bslmf::MovableRefUtil::move(nextRead->d_value.object());
1064#else
1065 *value = nextRead->d_value.object();
1066#endif
1067
1068 return 0;
1069}
1070
1071template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1073 const TYPE& value)
1074{
1075 Node *target = pushBackHelper();
1076
1077 if (0 == target) {
1078 return e_DISABLED; // RETURN
1079 }
1080
1083 ATOMIC_OP,
1084 MUTEX,
1085 CONDITION>,
1086 Node> proctor(this, target);
1087
1088 bslalg::ScalarPrimitives::copyConstruct(target->d_value.address(),
1089 value,
1090 allocator());
1091
1092 proctor.release();
1093
1094 int nodeState = ATOMIC_OP::swapIntAcqRel(&target->d_state, e_READABLE);
1095 if (e_WRITABLE_AND_BLOCKED == nodeState) {
1096 {
1097 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1098 }
1099 d_readCondition.signal();
1100 }
1101
1102 return 0;
1103}
1104
1105template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1108{
1109 Node *target = pushBackHelper();
1110
1111 if (0 == target) {
1112 return e_DISABLED; // RETURN
1113 }
1114
1117 ATOMIC_OP,
1118 MUTEX,
1119 CONDITION>,
1120 Node> proctor(this, target);
1121
1122 TYPE& dummy = value;
1123 bslalg::ScalarPrimitives::moveConstruct(target->d_value.address(),
1124 dummy,
1125 allocator());
1126
1127 proctor.release();
1128
1129 int nodeState = ATOMIC_OP::swapIntAcqRel(&target->d_state, e_READABLE);
1130 if (e_WRITABLE_AND_BLOCKED == nodeState) {
1131 {
1132 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1133 }
1134 d_readCondition.signal();
1135 }
1136
1137 return 0;
1138}
1139
1140template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1142{
1143 int count = 0;
1144
1145 bsls::Types::Int64 reclaim = 0;
1146
1147 Node *nextRead =
1148 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1149 int nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1150 while (e_READABLE == nodeState || e_RECLAIM == nodeState) {
1151 if (e_READABLE == nodeState) {
1152 nextRead->d_value.object().~TYPE();
1153 }
1154 else {
1155 ++reclaim;
1156 }
1157 ATOMIC_OP::setIntRelease(&nextRead->d_state, e_WRITABLE);
1158 nextRead =
1159 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&nextRead->d_next));
1160 ATOMIC_OP::setPtrRelease(&d_nextRead, nextRead);
1161 nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1162 ++count;
1163 }
1164
1165 ATOMIC_OP::addInt64AcqRel(&d_capacity, reclaim);
1166 ATOMIC_OP::addInt64AcqRel(&d_state, k_AVAILABLE_INC * count);
1167
1168 {
1169 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1170 }
1171 d_emptyCondition.broadcast();
1172}
1173
1174template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1176 TYPE *value)
1177{
1178 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
1179 if (1 == (generation & 1)) {
1180 return e_DISABLED; // RETURN
1181 }
1182
1183 Node *nextRead =
1184 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1185 int nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1186
1187 while (e_RECLAIM == nodeState) {
1188 ATOMIC_OP::addInt64AcqRel(&d_capacity, 1);
1189 popComplete(false);
1190 nextRead = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1191 nodeState = ATOMIC_OP::getIntAcquire(&nextRead->d_state);
1192 }
1193
1194 if (e_READABLE != nodeState) {
1195 return e_EMPTY; // RETURN
1196 }
1197
1200 ATOMIC_OP,
1201 MUTEX,
1202 CONDITION> > guard(this);
1203
1204#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
1205 *value = bslmf::MovableRefUtil::move(nextRead->d_value.object());
1206#else
1207 *value = nextRead->d_value.object();
1208#endif
1209
1210 return 0;
1211}
1212
1213template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1215 const TYPE& value)
1216{
1217 return pushBack(value);
1218}
1219
1220template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1226
1227 // Enqueue/Dequeue State
1228
1229template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1232{
1233 incrementUntil(&d_popFrontDisabled, 1);
1234
1235 {
1236 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1237 }
1238 d_readCondition.signal();
1239
1240 {
1241 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1242 }
1243 d_emptyCondition.broadcast();
1244}
1245
1246template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1249{
1250 incrementUntil(&d_pushBackDisabled, 1);
1251}
1252
1253template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1256{
1257 incrementUntil(&d_popFrontDisabled, 0);
1258}
1259
1260template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1263{
1264 incrementUntil(&d_pushBackDisabled, 0);
1265}
1266
1267// ACCESSORS
1268template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1271{
1272 return ATOMIC_OP::getInt64Acquire(&d_capacity) ==
1273 available(ATOMIC_OP::getInt64Acquire(&d_state));
1274}
1275
1276template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1281
1282template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1285{
1286 return 1 == (ATOMIC_OP::getUintAcquire(&d_popFrontDisabled) & 1);
1287}
1288
1289template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1292{
1293 return 1 == (ATOMIC_OP::getUintAcquire(&d_pushBackDisabled) & 1);
1294}
1295
1296template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1299{
1300 bsls::Types::Int64 avail = available(ATOMIC_OP::getInt64Acquire(&d_state));
1301 return static_cast<bsl::size_t>(
1302 avail > 0
1303 ? ATOMIC_OP::getInt64Acquire(&d_capacity) - avail
1304 : ATOMIC_OP::getInt64Acquire(&d_capacity));
1305}
1306
1307template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1310{
1311 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
1312 if (1 == (generation & 1)) {
1313 return e_DISABLED; // RETURN
1314 }
1315
1316 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1317
1318 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
1319 while (ATOMIC_OP::getInt64Acquire(&d_capacity) != available(state)) {
1320 if (generation != ATOMIC_OP::getUintAcquire(&d_popFrontDisabled)) {
1321 return e_DISABLED; // RETURN
1322 }
1323 d_emptyCondition.wait(&d_emptyMutex);
1324 state = ATOMIC_OP::getInt64Acquire(&d_state);
1325 }
1326
1327 return 0;
1328}
1329
1330 // Aspects
1331
1332template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1334 allocator() const
1335{
1336 return d_allocator.allocator();
1337}
1338
1339} // close package namespace
1340
1341
1342#endif
1343
1344// ----------------------------------------------------------------------------
1345// Copyright 2019 Bloomberg Finance L.P.
1346//
1347// Licensed under the Apache License, Version 2.0 (the "License");
1348// you may not use this file except in compliance with the License.
1349// You may obtain a copy of the License at
1350//
1351// http://www.apache.org/licenses/LICENSE-2.0
1352//
1353// Unless required by applicable law or agreed to in writing, software
1354// distributed under the License is distributed on an "AS IS" BASIS,
1355// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1356// See the License for the specific language governing permissions and
1357// limitations under the License.
1358// ----------------------------- END-OF-FILE ----------------------------------
1359
1360/** @} */
1361/** @} */
1362/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlcc_singleconsumerqueueimpl.h:249
~SingleConsumerQueueImpl_AllocateLockGuard()
Destroy this object and invoke the managed queue's.
Definition bdlcc_singleconsumerqueueimpl.h:678
Definition bdlcc_singleconsumerqueueimpl.h:174
~SingleConsumerQueueImpl_MarkReclaimProctor()
Definition bdlcc_singleconsumerqueueimpl.h:631
void release()
Definition bdlcc_singleconsumerqueueimpl.h:640
Definition bdlcc_singleconsumerqueueimpl.h:215
~SingleConsumerQueueImpl_PopCompleteGuard()
Definition bdlcc_singleconsumerqueueimpl.h:659
Definition bdlcc_singleconsumerqueueimpl.h:286
SingleConsumerQueueImpl(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_singleconsumerqueueimpl.h:944
bool isPopFrontDisabled() const
Definition bdlcc_singleconsumerqueueimpl.h:1284
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_singleconsumerqueueimpl.h:1221
int waitUntilEmpty() const
Definition bdlcc_singleconsumerqueueimpl.h:1309
int tryPushBack(const TYPE &value)
Definition bdlcc_singleconsumerqueueimpl.h:1214
~SingleConsumerQueueImpl()
Definition bdlcc_singleconsumerqueueimpl.h:983
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_singleconsumerqueueimpl.h:1334
bool isPushBackDisabled() const
Definition bdlcc_singleconsumerqueueimpl.h:1291
void disablePopFront()
Definition bdlcc_singleconsumerqueueimpl.h:1231
SingleConsumerQueueImpl(bslma::Allocator *basicAllocator=0)
Definition bdlcc_singleconsumerqueueimpl.h:918
void disablePushBack()
Definition bdlcc_singleconsumerqueueimpl.h:1248
int tryPopFront(TYPE *value)
Definition bdlcc_singleconsumerqueueimpl.h:1175
bsl::size_t numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_singleconsumerqueueimpl.h:1298
void enablePushBack()
Definition bdlcc_singleconsumerqueueimpl.h:1262
int pushBack(const TYPE &value)
Definition bdlcc_singleconsumerqueueimpl.h:1072
TYPE value_type
Definition bdlcc_singleconsumerqueueimpl.h:462
bool isEmpty() const
Definition bdlcc_singleconsumerqueueimpl.h:1270
void removeAll()
Definition bdlcc_singleconsumerqueueimpl.h:1141
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_singleconsumerqueueimpl.h:1106
bool isFull() const
Definition bdlcc_singleconsumerqueueimpl.h:1277
int popFront(TYPE *value)
Definition bdlcc_singleconsumerqueueimpl.h:1009
void enablePopFront()
Definition bdlcc_singleconsumerqueueimpl.h:1255
Definition bdlma_infrequentdeleteblocklist.h:245
void * allocate(bsls::Types::size_type size)
Definition bslma_allocator.h:545
Definition bslmf_movableref.h:752
Definition bslmt_lockguard.h:234
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlcc_boundedqueue.h:270
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1660
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1617
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
static void yield()
Definition bslmt_threadutil.h:1100
long long Int64
Definition bsls_types.h:134
Definition bsls_objectbuffer.h:277