BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_singleproducerqueueimpl.h
Go to the documentation of this file.
1/// @file bdlcc_singleproducerqueueimpl.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_singleproducerqueueimpl.h -*-C++-*-
8
9#ifndef INCLUDED_BDLCC_SINGLEPRODUCERQUEUEIMPL
10#define INCLUDED_BDLCC_SINGLEPRODUCERQUEUEIMPL
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bdlcc_singleproducerqueueimpl bdlcc_singleproducerqueueimpl
16/// @brief Provide a testable thread-aware single producer queue of values.
17/// @addtogroup bdl
18/// @{
19/// @addtogroup bdlcc
20/// @{
21/// @addtogroup bdlcc_singleproducerqueueimpl
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bdlcc_singleproducerqueueimpl-purpose"> Purpose</a>
26/// * <a href="#bdlcc_singleproducerqueueimpl-classes"> Classes </a>
27/// * <a href="#bdlcc_singleproducerqueueimpl-description"> Description </a>
28/// * <a href="#bdlcc_singleproducerqueueimpl-exception-safety"> Exception safety </a>
29/// * <a href="#bdlcc_singleproducerqueueimpl-move-semantics-in-c-03"> Move Semantics in C++03 </a>
30/// * <a href="#bdlcc_singleproducerqueueimpl-memory-usage"> Memory Usage </a>
31/// * <a href="#bdlcc_singleproducerqueueimpl-usage"> Usage </a>
32///
33/// # Purpose {#bdlcc_singleproducerqueueimpl-purpose}
34/// Provide a testable thread-aware single producer queue of values.
35///
36/// # Classes {#bdlcc_singleproducerqueueimpl-classes}
37///
38/// - bdlcc::SingleProducerQueueImpl: thread-aware single producer `TYPE` queue
39///
40/// # Description {#bdlcc_singleproducerqueueimpl-description}
41/// This component defines a type,
42/// `bdlcc::SingleProducerQueueImpl`, that provides an efficient, thread-aware
43/// queue of values assuming a single producer (the use of `pushBack` and
44/// `tryPushBack` is done by one thread or a group of threads using external
45/// synchronization). The behavior of the methods `pushBack` and `tryPushBack`
46/// is undefined unless the use is by a single producer. This class is ideal
47/// for synchronization and communication between threads in a producer-consumer
48/// model when there is only one producer thread.
49///
50/// The queue provides `pushBack` and `popFront` methods for pushing data into
51/// the queue and popping data from the queue. The queue will allocate memory
52/// as necessary to accommodate `pushBack` invocations (`pushBack` will never
53/// block and is provided for consistency with other containers). When the
54/// queue is empty, the `popFront` methods block until data appears in the
55/// queue. Non-blocking methods `tryPushBack` and `tryPopFront` are also
56/// provided. The `tryPopFront` method fails immediately, returning a non-zero
57/// value, if the queue is empty.
58///
59/// The queue may be placed into a "enqueue disabled" state using the
60/// `disablePushBack` method. When disabled, `pushBack` and `tryPushBack` fail
61/// immediately and return an error code. The queue may be restored to normal
62/// operation with the `enablePushBack` method.
63///
64/// The queue may be placed into a "dequeue disabled" state using the
65/// `disablePopFront` method. When dequeue disabled, `popFront` and
66/// `tryPopFront` fail immediately and return an error code. Any threads
67/// blocked in `popFront` when the queue is dequeue disabled return from
68/// `popFront` immediately and return an error code.
69///
70/// ## Exception safety {#bdlcc_singleproducerqueueimpl-exception-safety}
71///
72///
73/// A `bdlcc::SingleProducerQueueImpl` is exception neutral, and all of the
74/// methods of `bdlcc::SingleProducerQueueImpl` provide the basic exception
75/// safety guarantee (see @ref bsldoc_glossary ).
76///
77/// ## Move Semantics in C++03 {#bdlcc_singleproducerqueueimpl-move-semantics-in-c-03}
78///
79///
80/// Move-only types are supported by `bdlcc::SingleProducerQueueImpl` on C++11
81/// platforms only (where `BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES` is defined),
82/// and are not supported on C++03 platforms. Unfortunately, in C++03, there
83/// are user types where a `bslmf::MovableRef` will not safely degrade to a
84/// lvalue reference when a move constructor is not available (types providing a
85/// constructor template taking any type), so `bslmf::MovableRefUtil::move`
86/// cannot be used directly on a user supplied template type. See internal bug
87/// report 99039150 for more information.
88///
89/// ## Memory Usage {#bdlcc_singleproducerqueueimpl-memory-usage}
90///
91///
92/// `bdlcc::SingleProducerQueueImpl` is most efficient when dealing with small
93/// objects or fundamental types (as a thread-safe container, its methods pass
94/// objects *by* *value*). We recommend large objects be stored as
95/// shared-pointers (or possibly raw pointers).
96///
97/// ## Usage {#bdlcc_singleproducerqueueimpl-usage}
98///
99///
100/// There is no usage example for this component since it is not meant for
101/// direct client use.
102/// @}
103/** @} */
104/** @} */
105
106/** @addtogroup bdl
107 * @{
108 */
109/** @addtogroup bdlcc
110 * @{
111 */
112/** @addtogroup bdlcc_singleproducerqueueimpl
113 * @{
114 */
115
116#include <bdlscm_version.h>
117
119
121#include <bslma_default.h>
123
124#include <bslmf_movableref.h>
126
127#include <bslmt_lockguard.h>
128#include <bslmt_threadutil.h>
129
130#include <bsls_assert.h>
131#include <bsls_objectbuffer.h>
132#include <bsls_types.h>
133
134#include <bsl_cstddef.h>
135
136
137namespace bdlcc {
138
139 // ==================================================
140 // class SingleProducerQueueImpl_ReleaseAllRawProctor
141 // ==================================================
142
143/// This class implements a proctor that, unless its `release` method has
144/// previously been invoked, automatically invokes `releaseAllRaw` on a
145/// `TYPE` upon destruction.
146///
147/// See @ref bdlcc_singleproducerqueueimpl
148template <class TYPE>
150
151 // DATA
152 TYPE *d_queue_p; // managed queue
153
154 private:
155 // NOT IMPLEMENTED
161
162 public:
163 // CREATORS
164
165 /// Create a `removeAll` proctor that conditionally manages the
166 /// specified `queue` (if non-zero).
168
169 /// Destroy this object and, if `release` has not been invoked, invoke
170 /// the managed queue's `releaseAllRaw` method.
172
173 // MANIPULATORS
174
175 /// Release from management the queue currently managed by this proctor.
176 /// If no queue, this method has no effect.
177 void release();
178};
179
180 // ==============================================
181 // class SingleProducerQueueImpl_PopCompleteGuard
182 // ==============================================
183
184/// This class implements a guard automatically invokes `popComplete` on a
185/// `NODE` upon destruction.
186///
187/// See @ref bdlcc_singleproducerqueueimpl
188template <class TYPE, class NODE>
190
191 // DATA
192 TYPE *d_queue_p; // managed queue owning the managed node
193 NODE *d_node_p; // managed node
194 bool d_isEmpty; // if true, the empty condition will be signalled
195
196 private:
197 // NOT IMPLEMENTED
203
204 public:
205 // CREATORS
206
207 /// Create a `popComplete` guard managing the specified `queue` and
208 /// `node` that will cause the empty condition to be signalled if the
209 /// specified `isEmpty` is `true`.
211 NODE *node,
212 bool isEmpty);
213
214 /// Destroy this object and invoke the `TYPE::popComplete` method with
215 /// the managed `node`.
217};
218
219 // =============================
220 // class SingleProducerQueueImpl
221 // =============================
222
223/// This class provides a thread-safe unbounded queue of values that assumes
224/// a single producer thread.
225///
226/// The types `ATOMIC_OP`, `MUTEX`, and `CONDITION` are exposed for testing.
227/// Typical usage is with `bsls::AtomicOperations` for `ATOMIC_OP`,
228/// `bslmt::Mutex` for `MUTEX`, and `bslmt::Condition` for `CONDITION`.
229///
230/// See @ref bdlcc_singleproducerqueueimpl
231template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
233
234 // PRIVATE CONSTANTS
235 enum {
236 // These value are used as values for `d_state` in `Node`. A node is
237 // writable at creation and after a read completes (when the single
238 // producer can write to the node). A node is readable after it is
239 // written (when the node can be read by a consumer). The states
240 // in-between these two states (e.g., writing) are not needed by this
241 // implementation of the queue.
242
243 e_READABLE, // node can be read
244 e_WRITABLE // node can be written
245 };
246
247 static const int k_POP_YIELD_SPIN = 10; // number of yield-spins to
248 // attempt before acquiring
249 // `d_readMutex`
250
251 // The following constants are used to maintain the queue's `d_state`
252 // value. See *Implementation* *Note* for details.
253
254 static const bsls::Types::Int64 k_BLOCKED_MASK = 0x0000000000ffffffLL;
255 static const bsls::Types::Int64 k_BLOCKED_INC = 0x0000000000000001LL;
256 static const bsls::Types::Int64 k_AVAILABLE_INC = 0x0000000001000000LL;
257 static const bsls::Types::Int64 k_AVAILABLE_MASK = 0xffffffffff000000LL;
258 static const int k_AVAILABLE_SHIFT = 24;
259
260 // PRIVATE TYPES
261 typedef typename ATOMIC_OP::AtomicTypes::Int AtomicInt;
262 typedef typename ATOMIC_OP::AtomicTypes::Uint AtomicUint;
263 typedef typename ATOMIC_OP::AtomicTypes::Int64 AtomicInt64;
264 typedef typename ATOMIC_OP::AtomicTypes::Pointer AtomicPointer;
265
266 template <class T>
267 struct QueueNode {
268 // PUBLIC DATA
269 bsls::ObjectBuffer<T> d_value; // stored value
270 AtomicInt d_state; // `e_READABLE` or `e_WRITABLE`
271 AtomicPointer d_next; // pointer to next node
272 };
273
274 typedef QueueNode<TYPE> Node;
275
276 // DATA
277 AtomicPointer d_nextWrite; // pointer to next write to node
278
279 AtomicPointer d_nextRead; // pointer to next read from node
280
281 MUTEX d_readMutex; // used with `d_readCondition` to
282 // block until an element is
283 // available for popping
284
285 CONDITION d_readCondition; // condition variable for popping
286 // threads
287
288 mutable MUTEX d_emptyMutex; // blocking point for producer
289 // during `waitUntilEmpty`
290
291 mutable CONDITION d_emptyCondition; // condition variable for producer
292 // during `waitUntilEmpty`
293
294 AtomicInt64 d_state; // bit pattern representing the
295 // state of the queue (see
296 // implementation notes)
297
298 AtomicUint d_popFrontDisabled; // is queue pop disabled and
299 // generation count; see
300 // *Implementation* *Note*
301
302 AtomicUint d_pushBackDisabled; // is queue push disabled and
303 // generation count; see
304 // *Implementation* *Note*
305
306 bslma::Allocator *d_allocator_p; // allocator, held not owned
307
308 // FRIENDS
311 ATOMIC_OP,
312 MUTEX,
313 CONDITION> >;
314
317 ATOMIC_OP,
318 MUTEX,
319 CONDITION>,
320 typename SingleProducerQueueImpl<TYPE,
321 ATOMIC_OP,
322 MUTEX,
323 CONDITION>::Node >;
324
325 // PRIVATE CLASS METHODS
326
327 /// Return `true` if the specified `state` implies all elements in the
328 /// associated queue will be used to complete currently active dequeue
329 /// operations.
330 static bool allElementsReserved(bsls::Types::Int64 state);
331
332 /// Return `true` if the specified `state` implies the associated queue
333 /// can supply an element to a thread blocked in a dequeue operation.
334 static bool canSupplyBlockedThread(bsls::Types::Int64 state);
335
336 /// Return `true` if the specified `state` implies the associated queue
337 /// can supply exactly one element to a thread blocked in a dequeue
338 /// operation.
339 static bool canSupplyOneBlockedThread(bsls::Types::Int64 state);
340
341 /// Return the available attribute from the specified `state`.
342 static bsls::Types::Int64 getAvailable(bsls::Types::Int64 state);
343
344 /// Return `true` if the specified `state` implies the associated queue
345 /// is empty, and `false` otherwise.
346 static bool isEmpty(bsls::Types::Int64 state);
347
348 /// Return `true` if the specified `state` implies the associated queue
349 /// will have one or more threads blocked in a dequeue operation.
350 static bool willHaveBlockedThread(bsls::Types::Int64 state);
351
352 // PRIVATE MANIPULATORS
353
354 /// If the specified `value` does not have its lowest-order bit set to
355 /// the value of the specified `bitValue`, increment `value` until it does.
356 ///
357 /// \note Note that this method is used to modify the generation counts
358 /// stored in `d_popFrontDisabled` and `d_pushBackDisabled`. See
359 /// *Implementation* *Note* for further details.
360 void incrementUntil(AtomicUint *value, unsigned int bitValue);
361
362 /// Destruct the value stored in the specified `node`, mark the `node`
363 /// writable, and if the specified `isEmpty` is `true` then signal the
364 /// queue empty condition. This method is used within `popFrontRaw` by
365 /// a guard to complete the reclamation of a node in the presence of an
366 /// exception.
367 void popComplete(Node *node, bool isEmpty);
368
369 /// Remove the element, without verifying the availability of the
370 /// element, from the front of this queue, load that element into the
371 /// specified `value`, and if the specified `isEmpty` is `true` then
372 /// signal the queue empty condition.
373 void popFrontRaw(TYPE* value, bool isEmpty);
374
375 /// Return all memory to the allocator. This method is intended to be
376 /// used by the destructor and to avoid a memory leak when there is an
377 /// exception during construction.
378 void releaseAllRaw();
379
380 private:
381 // NOT IMPLEMENTED
382 SingleProducerQueueImpl(const SingleProducerQueueImpl&);
383 SingleProducerQueueImpl& operator=(const SingleProducerQueueImpl&);
384
385 public:
386 // TRAITS
387 BSLMF_NESTED_TRAIT_DECLARATION(SingleProducerQueueImpl,
388 bslma::UsesBslmaAllocator);
389
390 // PUBLIC TYPES
391 typedef TYPE value_type; // The type for elements.
392
393 // PUBLIC CONSTANTS
394 enum {
395 e_SUCCESS = 0, // must be 0
396 e_EMPTY = -1,
397 e_DISABLED = -2
398 };
399
400 // CREATORS
401
402 /// Create a thread-aware queue. Optionally specify a `basicAllocator`
403 /// used to supply memory. If `basicAllocator` is 0, the currently
404 /// installed default allocator is used.
405 explicit SingleProducerQueueImpl(bslma::Allocator *basicAllocator = 0);
406
407 /// Create a thread-aware queue with, at least, the specified
408 /// `capacity`. Optionally specify a `basicAllocator` used to supply
409 /// memory. If `basicAllocator` is 0, the currently installed default
410 /// allocator is used.
411 SingleProducerQueueImpl(bsl::size_t capacity,
412 bslma::Allocator *basicAllocator = 0);
413
414 /// Destroy this object.
416
417 // MANIPULATORS
418
419 /// Remove the element from the front of this queue and load that
420 /// element into the specified `value`. If the queue is empty, block
421 /// until it is not empty. Return 0 on success, and a non-zero value
422 /// otherwise. Specifically, return `e_DISABLED` if
423 /// `isPopFrontDisabled()`. On failure, `value` is not changed.
424 /// Threads blocked due to the queue being empty will return
425 /// `e_DISABLED` if `disablePopFront` is invoked.
426 int popFront(TYPE *value);
427
428 /// Append the specified `value` to the back of this queue. Return 0 on
429 /// success, and a non-zero value otherwise. Specifically, return
430 /// `e_DISABLED` if `isPushBackDisabled()`.
431 ///
432 /// \pre The behavior is undefined unless the invoker of this method is the single producer.
433 int pushBack(const TYPE& value);
434
435 /// Append the specified move-insertable `value` to the back of this
436 /// queue. `value` is left in a valid but unspecified state. Return 0
437 /// on success, and a non-zero value otherwise. Specifically, return
438 /// `e_DISABLED` if `isPushBackDisabled()`. On failure, `value` is not changed.
439 ///
440 /// \pre The behavior is undefined unless the invoker of this
441 /// method is the single producer.
443
444 /// Remove all items currently in this queue.
445 /// \note Note that this operation
446 /// is not atomic; if other threads are concurrently pushing items into
447 /// the queue the result of `numElements()` after this function returns
448 /// is not guaranteed to be 0.
449 void removeAll();
450
451 /// Attempt to remove the element from the front of this queue without
452 /// blocking, and, if successful, load the specified `value` with the
453 /// removed element. Return 0 on success, and a non-zero value
454 /// otherwise. Specifically, return `e_DISABLED` if
455 /// `isPopFrontDisabled()`, and `e_EMPTY` if `!isPopFrontDisabled()` and
456 /// the queue was empty. On failure, `value` is not changed.
457 int tryPopFront(TYPE *value);
458
459 /// Append the specified `value` to the back of this queue. Return 0 on
460 /// success, and a non-zero value otherwise. Specifically, return
461 /// `e_DISABLED` if `isPushBackDisabled()`.
462 ///
463 /// \pre The behavior is undefined unless the invoker of this method is the single producer.
464 int tryPushBack(const TYPE& value);
465
466 /// Append the specified move-insertable `value` to the back of this
467 /// queue. `value` is left in a valid but unspecified state. Return 0
468 /// on success, and a non-zero value otherwise. Specifically, return
469 /// `e_DISABLED" if `isPushBackDisabled()`. On failure, `value' is not changed.
470 ///
471 /// \pre The behavior is undefined unless the invoker of this
472 /// method is the single producer.
474
475 // Enqueue/Dequeue State
476
477 /// Disable dequeueing from this queue. All subsequent invocations of
478 /// `popFront` or `tryPopFront` will fail immediately. All blocked
479 /// invocations of `popFront` and `waitUntilEmpty` will fail
480 /// immediately. If the queue is already dequeue disabled, this method
481 /// has no effect.
483
484 /// Disable enqueueing into this queue. All subsequent invocations of
485 /// `pushBack` or `tryPushBack` will fail immediately. All blocked
486 /// invocations of `pushBack` will fail immediately. If the queue is
487 /// already enqueue disabled, this method has no effect.
489
490 /// Enable dequeueing. If the queue is not dequeue disabled, this call
491 /// has no effect.
493
494 /// Enable queuing. If the queue is not enqueue disabled, this call has
495 /// no effect.
497
498 // ACCESSORS
499
500 /// Return `true` if this queue is empty (has no elements), or `false`
501 /// otherwise.
502 bool isEmpty() const;
503
504 /// Return `true` if this queue is full (has no available capacity), or `false` otherwise.
505 ///
506 /// \note Note that for unbounded queues, this method
507 /// always returns `false`.
508 bool isFull() const;
509
510 /// Return `true` if this queue is dequeue disabled, and `false` otherwise.
511 ///
512 /// \note Note that the queue is created in the "dequeue enabled"
513 /// state.
514 bool isPopFrontDisabled() const;
515
516 /// Return `true` if this queue is enqueue disabled, and `false` otherwise.
517 ///
518 /// \note Note that the queue is created in the "enqueue enabled"
519 /// state.
520 bool isPushBackDisabled() const;
521
522 /// Returns the number of elements currently in this queue.
523 bsl::size_t numElements() const;
524
525 /// Block until all the elements in this queue are removed. Return 0 on
526 /// success, and a non-zero value otherwise. Specifically, return
527 /// `e_DISABLED` if `!isEmpty() && isPopFrontDisabled()`. A blocked
528 /// thread waiting for the queue to empty will return `e_DISABLED` if
529 /// `disablePopFront` is invoked.
530 int waitUntilEmpty() const;
531
532 // Aspects
533
534 /// Return the allocator used by this object to supply memory.
536};
537
538// ============================================================================
539// INLINE DEFINITIONS
540// ============================================================================
541
542 // --------------------------------------------------
543 // class SingleProducerQueueImpl_ReleaseAllRawProctor
544 // --------------------------------------------------
545
546// CREATORS
547template <class TYPE>
553
554template <class TYPE>
557{
558 if (d_queue_p) {
559 d_queue_p->releaseAllRaw();
560 }
561}
562
563// MANIPULATORS
564template <class TYPE>
569
570 // ----------------------------------------------
571 // class SingleProducerQueueImpl_PopCompleteGuard
572 // ----------------------------------------------
573
574// CREATORS
575template <class TYPE, class NODE>
578 NODE *node,
579 bool isEmpty)
580: d_queue_p(queue)
581, d_node_p(node)
582, d_isEmpty(isEmpty)
583{
584}
585
586template <class TYPE, class NODE>
592
593 // -----------------------------
594 // class SingleProducerQueueImpl
595 // -----------------------------
596
597// PRIVATE CLASS METHODS
598template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
599inline
602{
603 return (state >> k_AVAILABLE_SHIFT) <= (state & k_BLOCKED_MASK);
604}
605
606template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
607inline
608bool SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
609 canSupplyBlockedThread(bsls::Types::Int64 state)
610{
611 return k_AVAILABLE_INC <= state && (state & k_BLOCKED_MASK);
612}
613
614template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
615inline
616bool SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
617 canSupplyOneBlockedThread(bsls::Types::Int64 state)
618{
619 return k_AVAILABLE_INC == (state & k_AVAILABLE_MASK)
620 && (state & k_BLOCKED_MASK);
621}
622
623template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
624inline
625bsls::Types::Int64 SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
626 getAvailable(bsls::Types::Int64 state)
627{
628 return state >> k_AVAILABLE_SHIFT;
629}
630
631template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
632inline
635{
636 return k_AVAILABLE_INC > state;
637}
638
639template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
640inline
641bool SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
642 willHaveBlockedThread(bsls::Types::Int64 state)
643{
644 return (state >> k_AVAILABLE_SHIFT) < (state & k_BLOCKED_MASK);
645}
646
647// PRIVATE MANIPULATORS
648template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
649void SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>
650 ::incrementUntil(AtomicUint *value, unsigned int bitValue)
651{
652 unsigned int state = ATOMIC_OP::getUintAcquire(value);
653 if (bitValue != (state & 1)) {
654 unsigned int expState;
655 do {
656 expState = state;
657 state = ATOMIC_OP::testAndSwapUintAcqRel(value,
658 state,
659 state + 1);
660 } while (state != expState && (bitValue != (state & 1)));
661 }
662}
663
664template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
665void SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
666 popComplete(Node *node, bool isEmpty)
667{
668 node->d_value.object().~TYPE();
669
670 ATOMIC_OP::setIntRelease(&node->d_state, e_WRITABLE);
671
672 if (isEmpty) {
673 {
674 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
675 }
676 d_emptyCondition.broadcast();
677 }
678
679}
680
681template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
682void SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
683 popFrontRaw(TYPE *value,
684 bool isEmpty)
685{
686 Node *readFrom =
687 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
688
689 Node *exp;
690 do {
691 Node *next =
692 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&readFrom->d_next));
693
694 exp = readFrom;
695 readFrom = static_cast<Node *>(ATOMIC_OP::testAndSwapPtrAcqRel(
696 &d_nextRead,
697 readFrom,
698 next));
699 } while (readFrom != exp);
700
701 SingleProducerQueueImpl_PopCompleteGuard<
702 SingleProducerQueueImpl <TYPE,
703 ATOMIC_OP,
704 MUTEX,
705 CONDITION>,
706 Node> guard(this, readFrom, isEmpty);
707
708#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
709 *value = bslmf::MovableRefUtil::move(readFrom->d_value.object());
710#else
711 *value = readFrom->d_value.object();
712#endif
713}
714
715template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
716void SingleProducerQueueImpl<TYPE, ATOMIC_OP, MUTEX, CONDITION>::
717 releaseAllRaw()
718{
719 Node *end = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextWrite));
720
721 if (end) {
722 Node *at = static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&end->d_next));
723
724 while (at != end) {
725 Node *next =
726 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&at->d_next));
727
728 if (e_WRITABLE != ATOMIC_OP::getIntAcquire(&at->d_state)) {
729 at->d_value.object().~TYPE();
730 }
731
732 d_allocator_p->deallocate(at);
733
734 at = next;
735 }
736
737 if (e_WRITABLE != ATOMIC_OP::getIntAcquire(&at->d_state)) {
738 at->d_value.object().~TYPE();
739 }
740
741 d_allocator_p->deallocate(at);
742 }
743}
744
745// CREATORS
746template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
749: d_readMutex()
750, d_readCondition()
751, d_emptyMutex()
752, d_emptyCondition()
753, d_allocator_p(bslma::Default::allocator(basicAllocator))
754{
755 ATOMIC_OP::initInt64(&d_state, 0); // there are no available elements, the
756 // enable/disable generation is
757 // initialized to zero, and there are
758 // no threads blocked in 'popFront'
759
760 ATOMIC_OP::initUint(&d_popFrontDisabled, 0);
761 ATOMIC_OP::initUint(&d_pushBackDisabled, 0);
762
763 ATOMIC_OP::initPointer(&d_nextWrite, 0);
764
766 TYPE,
767 ATOMIC_OP,
768 MUTEX,
769 CONDITION> > proctor(this);
770
771 Node *n1 = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
772 ATOMIC_OP::initInt(&n1->d_state, e_WRITABLE);
773 ATOMIC_OP::initPointer(&n1->d_next, n1);
774
775 ATOMIC_OP::setPtrRelease(&d_nextWrite, n1);
776
777 ATOMIC_OP::initPointer(&d_nextRead, n1);
778
779 Node *n2 = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
780 ATOMIC_OP::initInt(&n2->d_state, e_WRITABLE);
781 ATOMIC_OP::initPointer(&n2->d_next, n1);
782 ATOMIC_OP::setPtrRelease(&n1->d_next, n2);
783
784 proctor.release();
785}
786
787template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
789 SingleProducerQueueImpl(bsl::size_t capacity,
790 bslma::Allocator *basicAllocator)
791: d_readMutex()
792, d_readCondition()
793, d_emptyMutex()
794, d_emptyCondition()
795, d_allocator_p(bslma::Default::allocator(basicAllocator))
796{
797 ATOMIC_OP::initInt64(&d_state, 0); // there are no available elements, the
798 // enable/disable generation is
799 // initialized to zero, and there are
800 // no threads blocked in 'popFront'
801
802 ATOMIC_OP::initUint(&d_popFrontDisabled, 0);
803 ATOMIC_OP::initUint(&d_pushBackDisabled, 0);
804
805 ATOMIC_OP::initPointer(&d_nextWrite, 0);
806
808 TYPE,
809 ATOMIC_OP,
810 MUTEX,
811 CONDITION> > proctor(this);
812
813 Node *n1 = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
814 ATOMIC_OP::initInt(&n1->d_state, e_WRITABLE);
815 ATOMIC_OP::initPointer(&n1->d_next, n1);
816
817 ATOMIC_OP::setPtrRelease(&d_nextWrite, n1);
818
819 ATOMIC_OP::initPointer(&d_nextRead, n1);
820
821 Node *n2 = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
822 ATOMIC_OP::initInt(&n2->d_state, e_WRITABLE);
823 ATOMIC_OP::initPointer(&n2->d_next, n1);
824 ATOMIC_OP::setPtrRelease(&n1->d_next, n2);
825
826 capacity = (2 <= capacity ? capacity : 2);
827
828 for (bsl::size_t i = 2; i < capacity; ++i) {
829 Node *n = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
830 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
831 ATOMIC_OP::initPointer(&n->d_next,
832 ATOMIC_OP::getPtrAcquire(&n2->d_next));
833
834 ATOMIC_OP::setPtrRelease(&n2->d_next, n);
835 }
836
837 proctor.release();
838}
839
840template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
846
847// MANIPULATORS
848template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
850 TYPE *value)
851{
852 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
853 if (1 == (generation & 1)) {
854 return e_DISABLED; // RETURN
855 }
856
857 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
858 -k_AVAILABLE_INC);
859
860 if (willHaveBlockedThread(state)) {
862 state = ATOMIC_OP::getInt64Acquire(&d_state);
863 if (willHaveBlockedThread(state)) {
864 {
865 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
866
867 state = ATOMIC_OP::addInt64NvAcqRel(
868 &d_state,
869 k_AVAILABLE_INC + k_BLOCKED_INC);
870
871 while (isEmpty(state)) {
872 if (generation !=
873 ATOMIC_OP::getUintAcquire(&d_popFrontDisabled)) {
874 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
875 return e_DISABLED; // RETURN
876 }
877 d_readCondition.wait(&d_readMutex);
878 state = ATOMIC_OP::getInt64Acquire(&d_state);
879 }
880
881 state = ATOMIC_OP::addInt64NvAcqRel(
882 &d_state,
883 -(k_AVAILABLE_INC + k_BLOCKED_INC));
884 }
885 if (canSupplyBlockedThread(state)) {
886 d_readCondition.signal();
887 }
888 }
889 }
890
891 popFrontRaw(value, isEmpty(state));
892
893 return 0;
894}
895
896template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
898 const TYPE& value)
899{
900 if (1 == (ATOMIC_OP::getUintAcquire(&d_pushBackDisabled) & 1)) {
901 return e_DISABLED; // RETURN
902 }
903
904 Node *nextWrite = static_cast<Node *>(
905 ATOMIC_OP::getPtrAcquire(&d_nextWrite));
906
907 Node *next = static_cast<Node *>(
908 ATOMIC_OP::getPtrAcquire(&nextWrite->d_next));
909
910 if (e_WRITABLE != ATOMIC_OP::getIntAcquire(&next->d_state)) {
911 Node *n = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
912
913 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
914 ATOMIC_OP::initPointer(&n->d_next, next);
915
916 ATOMIC_OP::setPtrRelease(&nextWrite->d_next, n);
917
918 next = n;
919 }
920
921 bslalg::ScalarPrimitives::copyConstruct(nextWrite->d_value.address(),
922 value,
923 d_allocator_p);
924
925 ATOMIC_OP::setIntRelease(&nextWrite->d_state, e_READABLE);
926 ATOMIC_OP::setPtrRelease(&d_nextWrite, next);
927
928 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
929 k_AVAILABLE_INC);
930
931 if (canSupplyOneBlockedThread(state)) {
932 {
933 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
934 }
935 d_readCondition.signal();
936 }
937
938 return 0;
939}
940
941template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
944{
945 if (1 == (ATOMIC_OP::getUintAcquire(&d_pushBackDisabled) & 1)) {
946 return e_DISABLED; // RETURN
947 }
948
949 Node *nextWrite = static_cast<Node *>(
950 ATOMIC_OP::getPtrAcquire(&d_nextWrite));
951
952 Node *next = static_cast<Node *>(
953 ATOMIC_OP::getPtrAcquire(&nextWrite->d_next));
954
955 if (e_WRITABLE != ATOMIC_OP::getIntAcquire(&next->d_state)) {
956 Node *n = static_cast<Node *>(d_allocator_p->allocate(sizeof(Node)));
957
958 ATOMIC_OP::initInt(&n->d_state, e_WRITABLE);
959 ATOMIC_OP::initPointer(&n->d_next, next);
960
961 ATOMIC_OP::setPtrRelease(&nextWrite->d_next, n);
962
963 next = n;
964 }
965
966 TYPE& dummy = value;
967 bslalg::ScalarPrimitives::moveConstruct(nextWrite->d_value.address(),
968 dummy,
969 d_allocator_p);
970
971 ATOMIC_OP::setIntRelease(&nextWrite->d_state, e_READABLE);
972 ATOMIC_OP::setPtrRelease(&d_nextWrite, next);
973
974 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
975 k_AVAILABLE_INC);
976
977 if (canSupplyOneBlockedThread(state)) {
978 {
979 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
980 }
981 d_readCondition.signal();
982 }
983
984 return 0;
985}
986
987template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
989 TYPE *value)
990{
991 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
992 if (1 == (generation & 1)) {
993 return e_DISABLED; // RETURN
994 }
995
996 // optimistically attempt to acquire resource (representing an element)
997
998 bsls::Types::Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state,
999 -k_AVAILABLE_INC);
1000
1001 while (willHaveBlockedThread(state)) {
1002
1003 // failed to acquire resource, must revert the change to 'd_state' or
1004 // acquire the resource (due to actions of other threads)
1005
1006 const bsls::Types::Int64 expState = state;
1007
1008 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
1009 state,
1010 state + k_AVAILABLE_INC);
1011 if (expState == state) {
1012 // reverted the change to 'd_state'
1013
1014 state += k_AVAILABLE_INC;
1015 if (canSupplyBlockedThread(state)) {
1016 {
1017 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1018 }
1019 d_readCondition.signal();
1020 }
1021 return e_EMPTY; // RETURN
1022 }
1023 }
1024
1025 popFrontRaw(value, isEmpty(state));
1026
1027 return 0;
1028}
1029
1030template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1032 const TYPE& value)
1033{
1034 return pushBack(value);
1035}
1036
1037template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1043
1044template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1046{
1047 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
1048 bsls::Types::Int64 expState;
1049
1050 do {
1051 if (allElementsReserved(state)) {
1052 return; // RETURN
1053 }
1054 expState = state;
1055 state = ATOMIC_OP::testAndSwapInt64AcqRel(
1056 &d_state,
1057 state,
1058 (state & ~k_AVAILABLE_MASK)
1059 | ( (state & k_BLOCKED_MASK)
1060 << k_AVAILABLE_SHIFT));
1061 } while (state != expState);
1062
1063 state = (state >> k_AVAILABLE_SHIFT) - (state & k_BLOCKED_MASK);
1064
1065 for (bsls::Types::Int64 i = 0; i < state; ++i) {
1066 Node *readFrom =
1067 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&d_nextRead));
1068 Node *exp;
1069 do {
1070 Node *next =
1071 static_cast<Node *>(ATOMIC_OP::getPtrAcquire(&readFrom->d_next));
1072
1073 exp = readFrom;
1074 readFrom = static_cast<Node *>(ATOMIC_OP::testAndSwapPtrAcqRel(
1075 &d_nextRead,
1076 readFrom,
1077 next));
1078 } while (readFrom != exp);
1079
1080 readFrom->d_value.object().~TYPE();
1081
1082 ATOMIC_OP::setIntRelease(&readFrom->d_state, e_WRITABLE);
1083 }
1084
1085 {
1086 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1087 }
1088 d_emptyCondition.broadcast();
1089}
1090
1091 // Enqueue/Dequeue State
1092
1093template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1096{
1097 incrementUntil(&d_popFrontDisabled, 1);
1098
1099 {
1100 bslmt::LockGuard<MUTEX> guard(&d_readMutex);
1101 }
1102 d_readCondition.broadcast();
1103
1104 {
1105 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1106 }
1107 d_emptyCondition.broadcast();
1108}
1109
1110template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1113{
1114 incrementUntil(&d_pushBackDisabled, 1);
1115}
1116
1117template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1120{
1121 incrementUntil(&d_popFrontDisabled, 0);
1122}
1123
1124template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1127{
1128 incrementUntil(&d_pushBackDisabled, 0);
1129}
1130
1131// ACCESSORS
1132template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1134 isEmpty() const
1135{
1136 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
1137 return isEmpty(state);
1138}
1139
1140template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1145
1146template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1149{
1150 return 1 == (ATOMIC_OP::getUintAcquire(&d_popFrontDisabled) & 1);
1151}
1152
1153template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1156{
1157 return 1 == (ATOMIC_OP::getUintAcquire(&d_pushBackDisabled) & 1);
1158}
1159
1160template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1162 numElements() const
1163{
1164 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
1165 bsls::Types::Int64 avail = getAvailable(state);
1166
1167 return avail >= 0 ? static_cast<bsl::size_t>(avail) : 0;
1168}
1169
1170template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1172 waitUntilEmpty() const
1173{
1174 unsigned int generation = ATOMIC_OP::getUintAcquire(&d_popFrontDisabled);
1175 if (1 == (generation & 1)) {
1176 return e_DISABLED; // RETURN
1177 }
1178
1179 bslmt::LockGuard<MUTEX> guard(&d_emptyMutex);
1180
1181 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
1182 while (!isEmpty(state)) {
1183 if (generation != ATOMIC_OP::getUintAcquire(&d_popFrontDisabled)) {
1184 return e_DISABLED; // RETURN
1185 }
1186 d_emptyCondition.wait(&d_emptyMutex);
1187 state = ATOMIC_OP::getInt64Acquire(&d_state);
1188 }
1189
1190 return 0;
1191}
1192
1193 // Aspects
1194
1195template <class TYPE, class ATOMIC_OP, class MUTEX, class CONDITION>
1201
1202} // close package namespace
1203
1204
1205#endif
1206
1207// ----------------------------------------------------------------------------
1208// Copyright 2019 Bloomberg Finance L.P.
1209//
1210// Licensed under the Apache License, Version 2.0 (the "License");
1211// you may not use this file except in compliance with the License.
1212// You may obtain a copy of the License at
1213//
1214// http://www.apache.org/licenses/LICENSE-2.0
1215//
1216// Unless required by applicable law or agreed to in writing, software
1217// distributed under the License is distributed on an "AS IS" BASIS,
1218// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1219// See the License for the specific language governing permissions and
1220// limitations under the License.
1221// ----------------------------- END-OF-FILE ----------------------------------
1222
1223/** @} */
1224/** @} */
1225/** @} */
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bdlcc_singleproducerqueueimpl.h:189
~SingleProducerQueueImpl_PopCompleteGuard()
Definition bdlcc_singleproducerqueueimpl.h:588
Definition bdlcc_singleproducerqueueimpl.h:149
~SingleProducerQueueImpl_ReleaseAllRawProctor()
Definition bdlcc_singleproducerqueueimpl.h:556
void release()
Definition bdlcc_singleproducerqueueimpl.h:565
Definition bdlcc_singleproducerqueueimpl.h:232
void disablePopFront()
Definition bdlcc_singleproducerqueueimpl.h:1095
bool isFull() const
Definition bdlcc_singleproducerqueueimpl.h:1141
SingleProducerQueueImpl(bslma::Allocator *basicAllocator=0)
Definition bdlcc_singleproducerqueueimpl.h:748
void enablePushBack()
Definition bdlcc_singleproducerqueueimpl.h:1126
SingleProducerQueueImpl(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_singleproducerqueueimpl.h:789
bool isEmpty() const
Definition bdlcc_singleproducerqueueimpl.h:1134
int waitUntilEmpty() const
Definition bdlcc_singleproducerqueueimpl.h:1172
void disablePushBack()
Definition bdlcc_singleproducerqueueimpl.h:1112
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_singleproducerqueueimpl.h:1038
void enablePopFront()
Definition bdlcc_singleproducerqueueimpl.h:1119
int popFront(TYPE *value)
Definition bdlcc_singleproducerqueueimpl.h:849
int tryPopFront(TYPE *value)
Definition bdlcc_singleproducerqueueimpl.h:988
int tryPushBack(const TYPE &value)
Definition bdlcc_singleproducerqueueimpl.h:1031
TYPE value_type
Definition bdlcc_singleproducerqueueimpl.h:391
bslma::Allocator * allocator() const
Return the allocator used by this object to supply memory.
Definition bdlcc_singleproducerqueueimpl.h:1197
int pushBack(const TYPE &value)
Definition bdlcc_singleproducerqueueimpl.h:897
bsl::size_t numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_singleproducerqueueimpl.h:1162
bool isPopFrontDisabled() const
Definition bdlcc_singleproducerqueueimpl.h:1148
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_singleproducerqueueimpl.h:942
void removeAll()
Definition bdlcc_singleproducerqueueimpl.h:1045
~SingleProducerQueueImpl()
Destroy this object.
Definition bdlcc_singleproducerqueueimpl.h:842
bool isPushBackDisabled() const
Definition bdlcc_singleproducerqueueimpl.h:1155
Definition bslma_allocator.h:545
virtual void * allocate(size_type size)=0
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
T::iterator end(T &container)
Definition bslstl_iterator.h:1621
Definition baljsn_encoder_testtypes.h:76
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1660
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1617
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
static void yield()
Definition bslmt_threadutil.h:1100
long long Int64
Definition bsls_types.h:134
Definition bsls_objectbuffer.h:277