BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_fixedqueue.h
Go to the documentation of this file.
1/// @file bdlcc_fixedqueue.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_fixedqueue.h -*-C++-*-
8#ifndef INCLUDED_BDLCC_FIXEDQUEUE
9#define INCLUDED_BDLCC_FIXEDQUEUE
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlcc_fixedqueue bdlcc_fixedqueue
15/// @brief Provide a thread-aware fixed-size queue of values.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlcc
19/// @{
20/// @addtogroup bdlcc_fixedqueue
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlcc_fixedqueue-purpose"> Purpose</a>
25/// * <a href="#bdlcc_fixedqueue-classes"> Classes </a>
26/// * <a href="#bdlcc_fixedqueue-description"> Description </a>
27/// * <a href="#bdlcc_fixedqueue-comparison-to-boundedqueue"> Comparison To BoundedQueue </a>
28/// * <a href="#bdlcc_fixedqueue-template-requirements"> Template Requirements </a>
29/// * <a href="#bdlcc_fixedqueue-exception-safety"> Exception safety </a>
30/// * <a href="#bdlcc_fixedqueue-memory-usage"> Memory Usage </a>
31/// * <a href="#bdlcc_fixedqueue-move-semantics-in-c-03"> Move Semantics in C++03 </a>
32/// * <a href="#bdlcc_fixedqueue-usage"> Usage </a>
33/// * <a href="#bdlcc_fixedqueue-example-1-a-simple-thread-pool"> Example 1: A Simple Thread Pool </a>
34///
35/// # Purpose {#bdlcc_fixedqueue-purpose}
36/// Provide a thread-aware fixed-size queue of values.
37///
38/// # Classes {#bdlcc_fixedqueue-classes}
39///
40/// - bdlcc::FixedQueue: thread-aware fixed-size queue of `TYPE` values
41///
42/// # Description {#bdlcc_fixedqueue-description}
43/// This component defines a type, `bdlcc::FixedQueue`, that
44/// provides an efficient, thread-aware fixed-size queue of values. This
45/// class is ideal for synchronization and communication between threads in a
46/// producer-consumer model. Under most cicrumstances developers should prefer
47/// the newer {bdlcc_boundedqueue} (see {Comparison to BoundedQueue}).
48///
49/// The queue provides `pushBack` and `popFront` methods for pushing data into
50/// the queue and popping it from the queue. In case of overflow (queue full
51/// when pushing), or underflow (queue empty when popping), the methods block
52/// until data or free space in the queue appears. Non-blocking methods
53/// `tryPushBack` and `tryPopFront` are also provided, which fail immediately
54/// returning a non-zero value in case of overflow or underflow.
55///
56/// The queue may be placed into a "disabled" state using the `disable` method.
57/// When disabled, `pushBack` and `tryPushBack` fail immediately (they do not
58/// block and any blocked invocations will fail immediately). The queue may be
59/// restored to normal operation with the `enable` method.
60///
61/// Unlike `bdlcc::Queue`, a fixed queue is not double-ended, there is no timed
62/// API like `timedPushBack` and `timedPopFront`, and no `forcePush` methods, as
63/// the queue capacity is fixed. Also, this component is not based on
64/// `bdlc::Queue`, so there is no API for direct access to the underlying queue.
65/// These limitations are a trade-off for significant gain in performance
66/// compared to `bdlcc::Queue`.
67///
68/// ## Comparison To BoundedQueue {#bdlcc_fixedqueue-comparison-to-boundedqueue}
69///
70///
71/// Both `bdlcc::FixedQueue` and `bdlcc::BoundedQueue` provide thread-aware
72/// bounded queues. Under most circumstances developers should prefer
73/// {bdlcc_boundedqueue}: it is newer, has additional features, and provides
74/// better performance under most circumstances. `bdlcc::BoundedQueue` is not
75/// quite a drop in replacement for `bdlcc::FixedQueue` so both types are
76/// currently maintained. There is additional information about
77/// performance of various queues in the article Concurrent Queue Evaluation
78/// (https://tinyurl.com/mr2un9f7).
79///
80/// ## Template Requirements {#bdlcc_fixedqueue-template-requirements}
81///
82///
83/// `bdlcc::FixedQueue` is a template that is parameterized on the type of
84/// element contained within the queue. The supplied template argument, `TYPE`,
85/// must provide both a default constructor and a copy constructors as well as
86/// an assignment operator. If the default constructor accepts a
87/// `bslma::Allocator*`, `TYPE` must declare the uses `bslma::Allocator` trait
88/// (see @ref bslma_usesbslmaallocator ) so that the allocator of the queue is
89/// propagated to the elements contained in the queue.
90///
91/// ## Exception safety {#bdlcc_fixedqueue-exception-safety}
92///
93///
94/// A `bdlcc::FixedQueue` is exception neutral, and all of the methods of
95/// `bdlcc::FixedQueue` provide the strong exception safety guarantee except for
96/// `pushBack` and `tryPushBack`, which provide the basic exception guarantee
97/// (see @ref bsldoc_glossary ).
98///
99/// ## Memory Usage {#bdlcc_fixedqueue-memory-usage}
100///
101///
102/// `bdlcc::FixedQueue` is most efficient when dealing with small objects or
103/// fundamental types (as a thread-safe container, its methods pass objects **by
104/// value**). We recommend:
105/// * Large objects be stored as shared-pointers (or possibly raw pointers).
106/// * Clients take care in specifying the queue capacity (specified in a number
107/// of objects, *not* a number of bytes).
108///
109/// Note that the implementation of `bdlcc::FixedQueue` currently creates a
110/// fixed size array of the contained object type.
111///
112/// ## Move Semantics in C++03 {#bdlcc_fixedqueue-move-semantics-in-c-03}
113///
114///
115/// Move-only types are supported by `FixedQueue` on C++11 platforms only (where
116/// `BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES` is defined), and are not supported
117/// on C++03 platforms. Unfortunately, in C++03, there are user types where a
118/// `bslmf::MovableRef` will not safely degrade to a lvalue reference when a
119/// move constructor is not available (types providing a constructor template
120/// taking any type), so `bslmf::MovableRefUtil::move` cannot be used directly
121/// on a user supplied template type. See internal bug report 99039150 for more
122/// information.
123///
124/// ## Usage {#bdlcc_fixedqueue-usage}
125///
126///
127/// This section illustrates intended use of this component.
128///
129/// ### Example 1: A Simple Thread Pool {#bdlcc_fixedqueue-example-1-a-simple-thread-pool}
130///
131///
132/// In the following example a `bdlcc::FixedQueue` is used to communicate
133/// between a single "producer" thread and multiple "consumer" threads. The
134/// "producer" will push work requests onto the queue, and each "consumer" will
135/// iteratively take a work request from the queue and service the request.
136/// This example shows a partial, simplified implementation of the
137/// `bdlmt::FixedThreadPool` class. See component @ref bdlmt_fixedthreadpool for
138/// more information.
139///
140/// First, we define a utility classes that handles a simple "work item":
141/// @code
142/// struct my_WorkData {
143/// // Work data...
144/// };
145///
146/// struct my_WorkRequest {
147/// enum RequestType {
148/// e_WORK = 1,
149/// e_STOP = 2
150/// };
151///
152/// RequestType d_type;
153/// my_WorkData d_data;
154/// // Work data...
155/// };
156/// @endcode
157/// Next, we provide a simple function to service an individual work item. The
158/// details are unimportant for this example:
159/// @code
160/// void myDoWork(my_WorkData& data)
161/// {
162/// // do some stuff...
163/// (void)data;
164/// }
165/// @endcode
166/// Then, we define a `myConsumer` function that will pop elements off the queue
167/// and process them. Note that the call to `queue->popFront()` will block
168/// until there is an element available on the queue. This function will be
169/// executed in multiple threads, so that each thread waits in
170/// `queue->popFront()`, and `bdlcc::FixedQueue` guarantees that each thread
171/// gets a unique element from the queue:
172/// @code
173/// void myConsumer(bdlcc::FixedQueue<my_WorkRequest> *queue)
174/// {
175/// while (1) {
176/// // 'popFront()' will wait for a 'my_WorkRequest' until available.
177///
178/// my_WorkRequest item = queue->popFront();
179/// if (item.d_type == my_WorkRequest::e_STOP) { break; }
180/// myDoWork(item.d_data);
181/// }
182/// }
183/// @endcode
184/// Finally, we define a `myProducer` function that serves multiple roles: it
185/// creates the `bdlcc::FixedQueue`, starts the consumer threads, and then
186/// produces and enqueues work items. When work requests are exhausted, this
187/// function enqueues one `e_STOP` item for each consumer queue. This `e_STOP`
188/// item indicates to the consumer thread to terminate its thread-handling
189/// function.
190///
191/// Note that, although the producer cannot control which thread `pop`s a
192/// particular work item, it can rely on the knowledge that each consumer thread
193/// will read a single `e_STOP` item and then terminate.
194/// @code
195/// void myProducer(int numThreads)
196/// {
197/// enum {
198/// k_MAX_QUEUE_LENGTH = 100,
199/// k_NUM_WORK_ITEMS = 1000
200/// };
201///
202/// bdlcc::FixedQueue<my_WorkRequest> queue(k_MAX_QUEUE_LENGTH);
203///
204/// bslmt::ThreadGroup consumerThreads;
205/// consumerThreads.addThreads(bdlf::BindUtil::bind(&myConsumer, &queue),
206/// numThreads);
207///
208/// for (int i = 0; i < k_NUM_WORK_ITEMS; ++i) {
209/// my_WorkRequest item;
210/// item.d_type = my_WorkRequest::e_WORK;
211/// item.d_data = my_WorkData(); // some stuff to do
212/// queue.pushBack(item);
213/// }
214///
215/// for (int i = 0; i < numThreads; ++i) {
216/// my_WorkRequest item;
217/// item.d_type = my_WorkRequest::e_STOP;
218/// queue.pushBack(item);
219/// }
220///
221/// consumerThreads.joinAll();
222/// }
223/// @endcode
224/// @}
225/** @} */
226/** @} */
227
228/** @addtogroup bdl
229 * @{
230 */
231/** @addtogroup bdlcc
232 * @{
233 */
234/** @addtogroup bdlcc_fixedqueue
235 * @{
236 */
237
238#include <bdlscm_version.h>
239
241
243
244#include <bslma_default.h>
247
248#include <bslmf_movableref.h>
250
251#include <bslmt_semaphore.h>
252#include <bslmt_threadutil.h>
253
254#include <bsls_assert.h>
255#include <bsls_atomic.h>
256#include <bsls_performancehint.h>
257#include <bsls_platform.h>
258#include <bsls_types.h>
259
260#include <bsl_algorithm.h>
261#include <bsl_vector.h>
262
263
264namespace bdlcc {
265
266 // ================
267 // class FixedQueue
268 // ================
269
270/// This class provides a thread-aware, lock-free, fixed-size queue of values.
271///
272/// See @ref bdlcc_fixedqueue
273template <class TYPE>
275
276 private:
277
278 // PRIVATE CONSTANTS
279 enum {
280 k_TYPE_PADDING = bslmt::Platform::e_CACHE_LINE_SIZE - sizeof(TYPE *),
281 k_SEMA_PADDING = bslmt::Platform::e_CACHE_LINE_SIZE -
282 sizeof(bslmt::Semaphore)
283 };
284
285 // DATA
286 TYPE *d_elements; // array of elements that comprise
287 // the fixed queue (array elements
288 // are manually constructed and
289 // destroyed, and empty elements
290 // hold uninitialized memory)
291
292 const char d_elementsPad[k_TYPE_PADDING];
293 // padding to prevent false sharing
295 d_impl; // index manager for managing the
296 // state of `d_elements`
297
298 bsls::AtomicInt d_numWaitingPoppers; // number of threads waiting on
299 // `d_popControlSema` to pop an
300 // element
301
302 bslmt::Semaphore d_popControlSema; // semaphore on which threads
303 // waiting to pop `wait`
304
305 const char d_popControlSemaPad[k_SEMA_PADDING];
306 // padding to prevent false sharing
307
308 bsls::AtomicInt d_numWaitingPushers; // number of threads waiting on
309 // `d_pushControlSema` to push an
310 // element
311
312 bslmt::Semaphore d_pushControlSema; // semaphore on which threads
313 // waiting to push `wait`
314
315 const char d_pushControlSemaPad[k_SEMA_PADDING];
316 // padding to prevent false sharing
317
318 bslma::Allocator *d_allocator_p; // allocator, held not owned
319
320 private:
321 // NOT IMPLEMENTED
322 FixedQueue(const FixedQueue&);
323 FixedQueue& operator=(const FixedQueue&);
324
325 // FRIENDS
326 template <class VAL> friend class FixedQueue_PushProctor;
327 template <class VAL> friend class FixedQueue_PopGuard;
328
329 public:
330 // TRAITS
332 // CREATORS
333
334 /// Create a thread-aware lock-free queue having the specified
335 /// `capacity`. Optionally specify a `basicAllocator` used to supply
336 /// memory. If `basicAllocator` is 0, the currently installed default allocator is used.
337 ///
338 /// \pre The behavior is undefined unless `0 < capacity`
339 /// and `capacity <= bdlcc::FixedQueueIndexManager::k_MAX_CAPACITY`.
340 explicit
341 FixedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
342
343 /// Destroy this object.
345
346 // MANIPULATORS
347
348 /// Append the specified `value` to the back of this queue, blocking
349 /// until either space is available - if necessary - or the queue is
350 /// disabled. Return 0 on success, and a nonzero value if the queue is
351 /// disabled.
352 int pushBack(const TYPE& value);
353
354 /// Append the specified move-insertable `value` to the back of this
355 /// queue, blocking until either space is available - if necessary - or
356 /// the queue is disabled. `value` is left in a valid but unspecified
357 /// state. Return 0 on success, and a nonzero value if the queue is
358 /// disabled.
360
361 /// Attempt to append the specified `value` to the back of this queue
362 /// without blocking. Return 0 on success, and a non-zero value if the
363 /// queue is full or disabled.
364 int tryPushBack(const TYPE& value);
365
366 /// Attempt to append the specified move-insertable `value` to the back
367 /// of this queue without blocking. `value` is left in a valid but
368 /// unspecified state. Return 0 on success, and a non-zero value if the
369 /// queue is full or disabled.
371
372 /// Remove the element from the front of this queue and load that
373 /// element into the specified `value`. If the queue is empty, block
374 /// until it is not empty.
375 void popFront(TYPE* value);
376
377 /// Remove the element from the front of this queue and return it's
378 /// value. If the queue is empty, block until it is not empty.
379 TYPE popFront();
380
381 /// Attempt to remove the element from the front of this queue without
382 /// blocking, and, if successful, load the specified `value` with the
383 /// removed element. Return 0 on success, and a non-zero value if queue
384 /// was empty. On failure, `value` is not changed.
385 int tryPopFront(TYPE *value);
386
387 /// Remove all items from this queue.
388 /// \note Note that this operation is not
389 /// atomic; if other threads are concurrently pushing items into the
390 /// queue the result of numElements() after this function returns is not
391 /// guaranteed to be 0.
392 void removeAll();
393
394 /// Disable this queue. All subsequent invocations of `pushBack` or
395 /// `tryPushBack` will fail immediately. All blocked invocations of
396 /// `pushBack` will fail immediately. If the queue is already disabled,
397 /// this method has no effect.
398 void disable();
399
400 /// Enable queuing. If the queue is not disabled, this call has no
401 /// effect.
402 void enable();
403
404 // ACCESSORS
405
406 /// Return the maximum number of elements that may be stored in this
407 /// queue.
408 int capacity() const;
409
410 /// Return `true` if this queue is empty (has no elements), or `false`
411 /// otherwise.
412 bool isEmpty() const;
413
414 /// Return `true` if this queue is enabled, and `false` otherwise.
415 ///
416 /// \note Note that the queue is created in the "enabled" state.
417 bool isEnabled() const;
418
419 /// Return `true` if this queue is full (when the number of elements
420 /// currently in this queue equals its capacity), or `false` otherwise.
421 bool isFull() const;
422
423 /// Returns the number of elements currently in this queue.
424 int numElements() const;
425
426 /// @deprecated Use @ref numElements() instead.
427 int length() const;
428
429 /// @deprecated Use @ref capacity() instead.
430 int size() const;
431
432};
433
434 // =========================
435 // class FixedQueue_PopGuard
436 // =========================
437
438/// This class provides a guard that, upon its destruction, will remove (pop)
439/// the indicated element from the `FixedQueue` object supplied at construction.
440///
441/// \note Note that this guard is used to provide exception safety
442/// when popping an element from a `FixedQueue` object.
443///
444/// See @ref bdlcc_fixedqueue
445template <class VALUE>
447
448 // DATA
449 FixedQueue<VALUE> *d_parent_p;
450 // object from which an element will be
451 // popped
452
453 unsigned int d_generation;
454 // generation count of cell being popped
455
456 unsigned int d_index;
457 // index of cell being popped
458
459 private:
460 // NOT IMPLEMENTED
462 FixedQueue_PopGuard& operator=(const FixedQueue_PopGuard&);
463 public:
464
465 // CREATORS
466
467 /// Create a guard that, upon its destruction, will update the state of
468 /// the specified `queue` to remove (pop) the element at the specified
469 /// `index` having the specified `generation`, and destroy that popped object.
470 ///
471 /// \pre The behavior is undefined unless `index` and `generation`
472 /// refer to a valid element in `queue` that the current thread has
473 /// acquired a reservation to pop (using
474 /// `FixedQueueIndexManager::reservePopIndex`).
476 unsigned int generation,
477 unsigned int index);
478
479 /// Update the state of the `FixedQueue` object supplied at construction
480 /// to remove (pop) the indicated element, and destroy the popped
481 /// object.
483};
484
485 // ============================
486 // class FixedQueue_PushProctor
487 // ============================
488
489/// This class provides a proctor that, unless the `release` method has been
490/// previously invoked, will remove and destroy all the elements from a
491/// `FixedQueue` object supplied at construction (putting that ring-buffer into a valid empty state) upon the proctor's destruction.
492///
493/// \note Note that this guard
494/// is used to provide exception safety when pushing an element into a
495/// `FixedQueue`.
496///
497/// See @ref bdlcc_fixedqueue
498template <class VALUE>
500
501 // DATA
502 FixedQueue<VALUE> *d_parent_p;
503 // object in which an element was pushed
504
505 unsigned int d_generation;
506 // generation of cell being pushed when an
507 // exception was thrown
508
509 unsigned int d_index;
510 // index of cell being pushed when an
511 // exception was thrown
512
513 private:
514 // NOT IMPLEMENTED
517
518 public:
519
520 // CREATORS
521
522 /// Create a proctor that manages the specified `queue` and, unless
523 /// `release` is called, will remove and destroy all the elements from
524 /// `queue` starting at the specified `index` in the specified `generation`.
525 ///
526 /// \pre The behavior is undefined unless `index` and
527 /// `generation` refers to a valid element in `queue`.
529 unsigned int generation,
530 unsigned int index);
531
532 /// Destroy this proctor and, if `release` was not called on this object,
533 /// remove and destroy all the elements from the `FixedQueue` object
534 /// supplied at construction.
536
537 // MANIPULATORS
538
539 /// Release from management the `FixedQueue` object supplied at
540 /// construction.
541 void release();
542
543};
544
545// ============================================================================
546// INLINE DEFINITIONS
547// ============================================================================
548
549// See the .cpp for an implementation note.
550
551 // ---------------------
552 // class FixedQueue
553 // ---------------------
554// CREATORS
555template <class TYPE>
556FixedQueue<TYPE>::FixedQueue(bsl::size_t capacity,
557 bslma::Allocator *basicAllocator)
558: d_elements()
559, d_elementsPad()
560, d_impl(capacity, basicAllocator)
561, d_numWaitingPoppers(0)
562, d_popControlSema(0)
563, d_popControlSemaPad()
564, d_numWaitingPushers(0)
565, d_pushControlSema(0)
566, d_pushControlSemaPad()
567, d_allocator_p(bslma::Default::allocator(basicAllocator))
568{
569 d_elements = static_cast<TYPE *>(
570 d_allocator_p->allocate(capacity * sizeof(TYPE)));
571}
572
573template <class TYPE>
575{
576 removeAll();
577 d_allocator_p->deallocate(d_elements);
578}
579
580template <class TYPE>
581int FixedQueue<TYPE>::tryPushBack(const TYPE& value)
582{
583 unsigned int generation;
584 unsigned int index;
585
586 // SYNCHRONIZATION POINT 1
587 //
588 // The following call to 'reservePushIndex' writes
589 // 'FixedQueueIndexManaged::d_pushIndex' with full sequential consistency,
590 // which guarantees the subsequent (relaxed) read from
591 // 'd_numWaitingPoppers' sees any waiting poppers from SYNCHRONIZATION
592 // POINT 1-Prime.
593
594 int retval = d_impl.reservePushIndex(&generation, &index);
595
596 if (0 != retval) {
597 return retval; // RETURN
598 }
599
600 // Copy the element into the cell. If an exception is thrown by the copy
601 // constructor, PushProctor will pop and discard items until reaching this
602 // cell, then mark this cell empty (without regard to its current state,
603 // which is WRITING (i.e., reserved)). That will leave the queue in a
604 // valid empty state.
605
606 FixedQueue_PushProctor<TYPE> guard(this, generation, index);
608 value,
609 d_allocator_p);
610 guard.release();
611 d_impl.commitPushIndex(generation, index);
612
613 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
614 d_popControlSema.post();
615 }
616
617 return 0;
618}
619
620template <class TYPE>
622{
623 unsigned int generation;
624 unsigned int index;
625
626 // SYNCHRONIZATION POINT 1
627 //
628 // The following call to `reservePushIndex` writes
629 // `FixedQueueIndexManaged::d_pushIndex` with full sequential consistency,
630 // which guarantees the subsequent (relaxed) read from
631 // `d_numWaitingPoppers` sees any waiting poppers from SYNCHRONIZATION
632 // POINT 1-Prime.
633
634 int retval = d_impl.reservePushIndex(&generation, &index);
635
636 if (0 != retval) {
637 return retval; // RETURN
638 }
639
640 // Move the element into the cell. If an exception is thrown by the move
641 // constructor, PushProctor will pop and discard items until reaching this
642 // cell, then mark this cell empty (without regard to its current state,
643 // which is WRITING (i.e., reserved)). That will leave the queue in a
644 // valid empty state.
645
646 FixedQueue_PushProctor<TYPE> guard(this, generation, index);
647 TYPE& dummy = value;
649 dummy,
650 d_allocator_p);
651 guard.release();
652 d_impl.commitPushIndex(generation, index);
653
654 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
655 d_popControlSema.post();
656 }
657
658 return 0;
659}
660
661template <class TYPE>
663{
664 unsigned int generation;
665 unsigned int index;
666
667 // SYNCHRONIZATION POINT 2
668 //
669 // The following call to `reservePopIndex` writes
670 // `FixedQueueIndexManaged::d_popIndex` with full sequential consistency,
671 // which guarantees the subsequent (relaxed) read from
672 // `d_numWaitingPoppers` sees any waiting poppers from SYNCHRONIZATION
673 // POINT 2-Prime.
674
675 int retval = d_impl.reservePopIndex(&generation, &index);
676
677 if (0 != retval) {
678 return retval; // RETURN
679 }
680
681 // Copy or move the element. `FixedQueue_PopGuard` will destroy original
682 // object, update the queue, and release a waiting pusher, even if the
683 // assignment operator throws.
684
685 FixedQueue_PopGuard<TYPE> guard(this, generation, index);
686 // Unfortunately, in C++03, there are user types where a MovableRef will
687 // not safely degrade to a lvalue reference when a move constructor is not
688 // available, so `move` cannot be used directly on a user supplied type.
689 // See internal bug report 99039150.
690#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
691 *value = bslmf::MovableRefUtil::move(d_elements[index]);
692#else
693 *value = d_elements[index];
694#endif
695 return 0;
696}
697
698// MANIPULATORS
699template <class TYPE>
700int FixedQueue<TYPE>::pushBack(const TYPE& value)
701{
702 int retval;
703 while (0 != (retval = tryPushBack(value))) {
704 if (retval < 0) {
705 // The queue is disabled.
706
707 return retval; // RETURN
708 }
709
710 d_numWaitingPushers.addRelaxed(1);
711
712 // SYNCHRONIZATION POINT 1-Prime
713 //
714 // The following call to `isFull` loads
715 // `FixedQueueIndexManager::d_pushIndex` with full sequential
716 // consistency, which is required to ensure the visibility of the
717 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
718 // 2.
719
720 if (isFull() && isEnabled()) {
721 d_pushControlSema.wait();
722 }
723
724 d_numWaitingPushers.addRelaxed(-1);
725 }
726
727 return 0;
728}
729
730template <class TYPE>
732{
733 int retval;
734 while (0 != (retval = tryPushBack(bslmf::MovableRefUtil::move(value)))) {
735 if (retval < 0) {
736 // The queue is disabled.
737
738 return retval; // RETURN
739 }
740
741 d_numWaitingPushers.addRelaxed(1);
742
743 // SYNCHRONIZATION POINT 1-Prime
744 //
745 // The following call to `isFull` loads
746 // `FixedQueueIndexManager::d_pushIndex` with full sequential
747 // consistency, which is required to ensure the visibility of the
748 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
749 // 2.
750
751 if (isFull() && isEnabled()) {
752 d_pushControlSema.wait();
753 }
754
755 d_numWaitingPushers.addRelaxed(-1);
756 }
757
758 return 0;
759}
760
761template <class TYPE>
763{
764 while (0 != tryPopFront(value)) {
765 d_numWaitingPoppers.addRelaxed(1);
766
767 // SYNCHRONIZATION POINT 2-Prime
768 //
769 // The following call to `isEmpty` loads
770 // `FixedQueueIndexManager::d_pushIndex` with full sequential
771 // consistency, which is required to ensure the visibility of the
772 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
773 // 2.
774
775 if (isEmpty()) {
776 d_popControlSema.wait();
777 }
778
779 d_numWaitingPoppers.addRelaxed(-1);
780 }
781}
782
783template <class TYPE>
785{
786 unsigned int generation;
787 unsigned int index;
788
789 while (0 != d_impl.reservePopIndex(&generation, &index)) {
790 d_numWaitingPoppers.addRelaxed(1);
791
792 if (isEmpty()) {
793 d_popControlSema.wait();
794 }
795
796 d_numWaitingPoppers.addRelaxed(-1);
797 }
798
799 // Copy the element. `FixedQueue_PopGuard` will destroy original object,
800 // update the queue, and release a waiting pusher, even if the copy
801 // constructor throws.
802
803 FixedQueue_PopGuard<TYPE> guard(this, generation, index);
804 // Unfortunately, in C++03, there are user types where a MovableRef will
805 // not safely degrade to a lvalue reference when a move constructor is not
806 // available, so `move` cannot be used directly on a user supplied type.
807 // See internal bug report 99039150.
808#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
809 return TYPE(bslmf::MovableRefUtil::move(d_elements[index]));
810#else
811 return TYPE(d_elements[index]);
812#endif
813}
814
815template <class TYPE>
817{
818 const int numItems = numElements();
819 int poppedItems = 0;
820 while (poppedItems++ < numItems) {
821 unsigned int index;
822 unsigned int generation;
823
824 if (0 != d_impl.reservePopIndex(&generation, &index)) {
825 break;
826 }
827
828 bslma::DestructionUtil::destroy(d_elements + index);
829 d_impl.commitPopIndex(generation, index);
830 }
831
832 int numWakeUps = bsl::min(poppedItems,
833 static_cast<int>(d_numWaitingPushers));
834 while (numWakeUps--) {
835 // Wake up waiting pushers.
836
837 d_pushControlSema.post();
838 }
839}
840
841template <class TYPE>
843{
844 d_impl.disable();
845
846 const int numWaitingPushers = d_numWaitingPushers;
847
848 for (int i = 0; i < numWaitingPushers; ++i) {
849 d_pushControlSema.post();
850 }
851}
852
853template <class TYPE>
854inline
856{
857 d_impl.enable();
858}
859
860// ACCESSORS
861template <class TYPE>
862inline
864{
865 return static_cast<int>(d_impl.capacity());
866}
867
868template <class TYPE>
869inline
871{
872 return (0 >= numElements());
873}
874
875template <class TYPE>
876inline
878{
879 return d_impl.isEnabled();
880}
881
882template <class TYPE>
883inline
885{
886 return (capacity() <= numElements());
887}
888
889template <class TYPE>
890inline
892{
893 return numElements();
894}
895
896template <class TYPE>
897inline
899{
900 return static_cast<int>(d_impl.length());
901}
902
903template <class TYPE>
904inline
906{
907 return static_cast<int>(capacity());
908}
909
910 // -------------------------
911 // class FixedQueue_PopGuard
912 // -------------------------
913
914// CREATORS
915template <class VALUE>
916inline
918 unsigned int generation,
919 unsigned int index)
920: d_parent_p(queue)
921, d_generation(generation)
922, d_index(index)
923{
924}
925
926template <class VALUE>
928{
929 // This popping thread currently has the cell at `d_index` (in
930 // `d_generation`) reserved for popping. Destroy the element at that
931 // position and then release the reservation. Wake up to 1 waiting pusher
932 // thread.
933
934 bslma::DestructionUtil::destroy(d_parent_p->d_elements + d_index);
935
936 d_parent_p->d_impl.commitPopIndex(d_generation, d_index);
937
938 // Notify pusher of available element.
939
941 d_parent_p->d_numWaitingPushers)) {
942 d_parent_p->d_pushControlSema.post();
943 }
944}
945
946 // ----------------------------
947 // class FixedQueue_PushProctor
948 // ----------------------------
949
950// CREATORS
951template <class VALUE>
952inline
954 FixedQueue<VALUE> *queue,
955 unsigned int generation,
956 unsigned int index)
957: d_parent_p(queue)
958, d_generation(generation)
959, d_index(index)
960{
961}
962
963template <class VALUE>
965{
966 if (d_parent_p) {
967 // This pushing thread currently has the cell at `d_index` reserved as
968 // `e_WRITING`. Dispose of all the elements up to `d_index`.
969
970 unsigned int generation, index;
971
972 // We will always have at least 1 popped item for the cell reserved for
973 // writing by the current thread.
974
975 int poppedItems = 1;
976 while (0 == d_parent_p->d_impl.reservePopIndexForClear(&generation,
977 &index,
978 d_generation,
979 d_index)) {
980 bslma::DestructionUtil::destroy(d_parent_p->d_elements + index);
981 ++poppedItems;
982
983 d_parent_p->d_impl.commitPopIndex(generation, index);
984 }
985
986 // Release the currently held pop index.
987
988 d_parent_p->d_impl.abortPushIndexReservation(d_generation, d_index);
989
990 while (poppedItems--) {
991 // Wake up waiting pushers.
992
993 d_parent_p->d_pushControlSema.post();
994 }
995 }
996}
997
998// MANIPULATORS
999template <class VALUE>
1000inline
1002{
1003 d_parent_p = 0;
1004}
1005
1006} // close package namespace
1007
1008
1009#endif
1010
1011// ----------------------------------------------------------------------------
1012// Copyright 2015 Bloomberg Finance L.P.
1013//
1014// Licensed under the Apache License, Version 2.0 (the "License");
1015// you may not use this file except in compliance with the License.
1016// You may obtain a copy of the License at
1017//
1018// http://www.apache.org/licenses/LICENSE-2.0
1019//
1020// Unless required by applicable law or agreed to in writing, software
1021// distributed under the License is distributed on an "AS IS" BASIS,
1022// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1023// See the License for the specific language governing permissions and
1024// limitations under the License.
1025// ----------------------------- END-OF-FILE ----------------------------------
1026
1027/** @} */
1028/** @} */
1029/** @} */
Definition bdlcc_fixedqueueindexmanager.h:257
Definition bdlcc_fixedqueue.h:446
~FixedQueue_PopGuard()
Definition bdlcc_fixedqueue.h:927
Definition bdlcc_fixedqueue.h:499
~FixedQueue_PushProctor()
Definition bdlcc_fixedqueue.h:964
void release()
Definition bdlcc_fixedqueue.h:1001
Definition bdlcc_fixedqueue.h:274
~FixedQueue()
Destroy this object.
Definition bdlcc_fixedqueue.h:574
bool isFull() const
Definition bdlcc_fixedqueue.h:884
void removeAll()
Definition bdlcc_fixedqueue.h:816
bool isEnabled() const
Definition bdlcc_fixedqueue.h:877
int size() const
Definition bdlcc_fixedqueue.h:905
int capacity() const
Definition bdlcc_fixedqueue.h:863
int length() const
Definition bdlcc_fixedqueue.h:891
int pushBack(const TYPE &value)
Definition bdlcc_fixedqueue.h:700
void enable()
Definition bdlcc_fixedqueue.h:855
int numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_fixedqueue.h:898
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_fixedqueue.h:621
TYPE popFront()
Definition bdlcc_fixedqueue.h:784
void disable()
Definition bdlcc_fixedqueue.h:842
bool isEmpty() const
Definition bdlcc_fixedqueue.h:870
BSLMF_NESTED_TRAIT_DECLARATION(FixedQueue, bslma::UsesBslmaAllocator)
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_fixedqueue.h:731
FixedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_fixedqueue.h:556
int tryPushBack(const TYPE &value)
Definition bdlcc_fixedqueue.h:581
int tryPopFront(TYPE *value)
Definition bdlcc_fixedqueue.h:662
void popFront(TYPE *value)
Definition bdlcc_fixedqueue.h:762
Definition bslma_allocator.h:545
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:752
Definition bslmt_semaphore.h:169
Definition bsls_atomic.h:744
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(expr)
Definition bsls_performancehint.h:452
Definition bdlcc_boundedqueue.h:270
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
Definition bslma_usesbslmaallocator.h:344
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
@ e_CACHE_LINE_SIZE
Definition bslmt_platform.h:214