BDE 4.14.0 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
337 /// allocator is used. The behavior is undefined unless `0 < capacity`
338 /// and `capacity <= bdlcc::FixedQueueIndexManager::k_MAX_CAPACITY`.
339 explicit
340 FixedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
341
342 /// Destroy this object.
344
345 // MANIPULATORS
346
347 /// Append the specified `value` to the back of this queue, blocking
348 /// until either space is available - if necessary - or the queue is
349 /// disabled. Return 0 on success, and a nonzero value if the queue is
350 /// disabled.
351 int pushBack(const TYPE& value);
352
353 /// Append the specified move-insertable `value` to the back of this
354 /// queue, blocking until either space is available - if necessary - or
355 /// the queue is disabled. `value` is left in a valid but unspecified
356 /// state. Return 0 on success, and a nonzero value if the queue is
357 /// disabled.
359
360 /// Attempt to append the specified `value` to the back of this queue
361 /// without blocking. Return 0 on success, and a non-zero value if the
362 /// queue is full or disabled.
363 int tryPushBack(const TYPE& value);
364
365 /// Attempt to append the specified move-insertable `value` to the back
366 /// of this queue without blocking. `value` is left in a valid but
367 /// unspecified state. Return 0 on success, and a non-zero value if the
368 /// queue is full or disabled.
370
371 /// Remove the element from the front of this queue and load that
372 /// element into the specified `value`. If the queue is empty, block
373 /// until it is not empty.
374 void popFront(TYPE* value);
375
376 /// Remove the element from the front of this queue and return it's
377 /// value. If the queue is empty, block until it is not empty.
378 TYPE popFront();
379
380 /// Attempt to remove the element from the front of this queue without
381 /// blocking, and, if successful, load the specified `value` with the
382 /// removed element. Return 0 on success, and a non-zero value if queue
383 /// was empty. On failure, `value` is not changed.
384 int tryPopFront(TYPE *value);
385
386 /// Remove all items from this queue. Note that this operation is not
387 /// atomic; if other threads are concurrently pushing items into the
388 /// queue the result of numElements() after this function returns is not
389 /// guaranteed to be 0.
390 void removeAll();
391
392 /// Disable this queue. All subsequent invocations of `pushBack` or
393 /// `tryPushBack` will fail immediately. All blocked invocations of
394 /// `pushBack` will fail immediately. If the queue is already disabled,
395 /// this method has no effect.
396 void disable();
397
398 /// Enable queuing. If the queue is not disabled, this call has no
399 /// effect.
400 void enable();
401
402 // ACCESSORS
403
404 /// Return the maximum number of elements that may be stored in this
405 /// queue.
406 int capacity() const;
407
408 /// Return `true` if this queue is empty (has no elements), or `false`
409 /// otherwise.
410 bool isEmpty() const;
411
412 /// Return `true` if this queue is enabled, and `false` otherwise. Note
413 /// that the queue is created in the "enabled" state.
414 bool isEnabled() const;
415
416 /// Return `true` if this queue is full (when the number of elements
417 /// currently in this queue equals its capacity), or `false` otherwise.
418 bool isFull() const;
419
420 /// Returns the number of elements currently in this queue.
421 int numElements() const;
422
423 /// [**DEPRECATED**] Invoke `numElements`.
424 int length() const;
425
426 /// [**DEPRECATED**] Invoke `capacity`.
427 int size() const;
428
429};
430
431 // =========================
432 // class FixedQueue_PopGuard
433 // =========================
434
435/// This class provides a guard that, upon its destruction, will remove (pop)
436/// the indicated element from the `FixedQueue` object supplied at
437/// construction. Note that this guard is used to provide exception safety
438/// when popping an element from a `FixedQueue` object.
439///
440/// See @ref bdlcc_fixedqueue
441template <class VALUE>
443
444 // DATA
445 FixedQueue<VALUE> *d_parent_p;
446 // object from which an element will be
447 // popped
448
449 unsigned int d_generation;
450 // generation count of cell being popped
451
452 unsigned int d_index;
453 // index of cell being popped
454
455 private:
456 // NOT IMPLEMENTED
458 FixedQueue_PopGuard& operator=(const FixedQueue_PopGuard&);
459 public:
460
461 // CREATORS
462
463 /// Create a guard that, upon its destruction, will update the state of
464 /// the specified `queue` to remove (pop) the element at the specified
465 /// `index` having the specified `generation`, and destroy that popped
466 /// object. The behavior is undefined unless `index` and `generation`
467 /// refer to a valid element in `queue` that the current thread has
468 /// acquired a reservation to pop (using
469 /// `FixedQueueIndexManager::reservePopIndex`).
471 unsigned int generation,
472 unsigned int index);
473
474 /// Update the state of the `FixedQueue` object supplied at construction
475 /// to remove (pop) the indicated element, and destroy the popped
476 /// object.
478};
479
480 // ============================
481 // class FixedQueue_PushProctor
482 // ============================
483
484/// This class provides a proctor that, unless the `release` method has been
485/// previously invoked, will remove and destroy all the elements from a
486/// `FixedQueue` object supplied at construction (putting that ring-buffer into
487/// a valid empty state) upon the proctor's destruction. Note that this guard
488/// is used to provide exception safety when pushing an element into a
489/// `FixedQueue`.
490///
491/// See @ref bdlcc_fixedqueue
492template <class VALUE>
494
495 // DATA
496 FixedQueue<VALUE> *d_parent_p;
497 // object in which an element was pushed
498
499 unsigned int d_generation;
500 // generation of cell being pushed when an
501 // exception was thrown
502
503 unsigned int d_index;
504 // index of cell being pushed when an
505 // exception was thrown
506
507 private:
508 // NOT IMPLEMENTED
511
512 public:
513
514 // CREATORS
515
516 /// Create a proctor that manages the specified `queue` and, unless
517 /// `release` is called, will remove and destroy all the elements from
518 /// `queue` starting at the specified `index` in the specified
519 /// `generation`. The behavior is undefined unless `index` and
520 /// `generation` refers to a valid element in `queue`.
522 unsigned int generation,
523 unsigned int index);
524
525 /// Destroy this proctor and, if `release` was not called on this object,
526 /// remove and destroy all the elements from the `FixedQueue` object
527 /// supplied at construction.
529
530 // MANIPULATORS
531
532 /// Release from management the `FixedQueue` object supplied at
533 /// construction.
534 void release();
535
536};
537
538// ============================================================================
539// INLINE DEFINITIONS
540// ============================================================================
541
542// See the .cpp for an implementation note.
543
544 // ---------------------
545 // class FixedQueue
546 // ---------------------
547// CREATORS
548template <class TYPE>
549FixedQueue<TYPE>::FixedQueue(bsl::size_t capacity,
550 bslma::Allocator *basicAllocator)
551: d_elements()
552, d_elementsPad()
553, d_impl(capacity, basicAllocator)
554, d_numWaitingPoppers(0)
555, d_popControlSema(0)
556, d_popControlSemaPad()
557, d_numWaitingPushers(0)
558, d_pushControlSema(0)
559, d_pushControlSemaPad()
560, d_allocator_p(bslma::Default::allocator(basicAllocator))
561{
562 d_elements = static_cast<TYPE *>(
563 d_allocator_p->allocate(capacity * sizeof(TYPE)));
564}
565
566template <class TYPE>
568{
569 removeAll();
570 d_allocator_p->deallocate(d_elements);
571}
572
573template <class TYPE>
574int FixedQueue<TYPE>::tryPushBack(const TYPE& value)
575{
576 unsigned int generation;
577 unsigned int index;
578
579 // SYNCHRONIZATION POINT 1
580 //
581 // The following call to 'reservePushIndex' writes
582 // 'FixedQueueIndexManaged::d_pushIndex' with full sequential consistency,
583 // which guarantees the subsequent (relaxed) read from
584 // 'd_numWaitingPoppers' sees any waiting poppers from SYNCHRONIZATION
585 // POINT 1-Prime.
586
587 int retval = d_impl.reservePushIndex(&generation, &index);
588
589 if (0 != retval) {
590 return retval; // RETURN
591 }
592
593 // Copy the element into the cell. If an exception is thrown by the copy
594 // constructor, PushProctor will pop and discard items until reaching this
595 // cell, then mark this cell empty (without regard to its current state,
596 // which is WRITING (i.e., reserved)). That will leave the queue in a
597 // valid empty state.
598
599 FixedQueue_PushProctor<TYPE> guard(this, generation, index);
601 value,
602 d_allocator_p);
603 guard.release();
604 d_impl.commitPushIndex(generation, index);
605
606 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
607 d_popControlSema.post();
608 }
609
610 return 0;
611}
612
613template <class TYPE>
615{
616 unsigned int generation;
617 unsigned int index;
618
619 // SYNCHRONIZATION POINT 1
620 //
621 // The following call to `reservePushIndex` writes
622 // `FixedQueueIndexManaged::d_pushIndex` with full sequential consistency,
623 // which guarantees the subsequent (relaxed) read from
624 // `d_numWaitingPoppers` sees any waiting poppers from SYNCHRONIZATION
625 // POINT 1-Prime.
626
627 int retval = d_impl.reservePushIndex(&generation, &index);
628
629 if (0 != retval) {
630 return retval; // RETURN
631 }
632
633 // Move the element into the cell. If an exception is thrown by the move
634 // constructor, PushProctor will pop and discard items until reaching this
635 // cell, then mark this cell empty (without regard to its current state,
636 // which is WRITING (i.e., reserved)). That will leave the queue in a
637 // valid empty state.
638
639 FixedQueue_PushProctor<TYPE> guard(this, generation, index);
640 TYPE& dummy = value;
642 dummy,
643 d_allocator_p);
644 guard.release();
645 d_impl.commitPushIndex(generation, index);
646
647 if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
648 d_popControlSema.post();
649 }
650
651 return 0;
652}
653
654template <class TYPE>
656{
657 unsigned int generation;
658 unsigned int index;
659
660 // SYNCHRONIZATION POINT 2
661 //
662 // The following call to `reservePopIndex` writes
663 // `FixedQueueIndexManaged::d_popIndex` with full sequential consistency,
664 // which guarantees the subsequent (relaxed) read from
665 // `d_numWaitingPoppers` sees any waiting poppers from SYNCHRONIZATION
666 // POINT 2-Prime.
667
668 int retval = d_impl.reservePopIndex(&generation, &index);
669
670 if (0 != retval) {
671 return retval; // RETURN
672 }
673
674 // Copy or move the element. `FixedQueue_PopGuard` will destroy original
675 // object, update the queue, and release a waiting pusher, even if the
676 // assignment operator throws.
677
678 FixedQueue_PopGuard<TYPE> guard(this, generation, index);
679 // Unfortunately, in C++03, there are user types where a MovableRef will
680 // not safely degrade to a lvalue reference when a move constructor is not
681 // available, so `move` cannot be used directly on a user supplied type.
682 // See internal bug report 99039150.
683#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
684 *value = bslmf::MovableRefUtil::move(d_elements[index]);
685#else
686 *value = d_elements[index];
687#endif
688 return 0;
689}
690
691// MANIPULATORS
692template <class TYPE>
693int FixedQueue<TYPE>::pushBack(const TYPE& value)
694{
695 int retval;
696 while (0 != (retval = tryPushBack(value))) {
697 if (retval < 0) {
698 // The queue is disabled.
699
700 return retval; // RETURN
701 }
702
703 d_numWaitingPushers.addRelaxed(1);
704
705 // SYNCHRONIZATION POINT 1-Prime
706 //
707 // The following call to `isFull` loads
708 // `FixedQueueIndexManager::d_pushIndex` with full sequential
709 // consistency, which is required to ensure the visibility of the
710 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
711 // 2.
712
713 if (isFull() && isEnabled()) {
714 d_pushControlSema.wait();
715 }
716
717 d_numWaitingPushers.addRelaxed(-1);
718 }
719
720 return 0;
721}
722
723template <class TYPE>
725{
726 int retval;
727 while (0 != (retval = tryPushBack(bslmf::MovableRefUtil::move(value)))) {
728 if (retval < 0) {
729 // The queue is disabled.
730
731 return retval; // RETURN
732 }
733
734 d_numWaitingPushers.addRelaxed(1);
735
736 // SYNCHRONIZATION POINT 1-Prime
737 //
738 // The following call to `isFull` loads
739 // `FixedQueueIndexManager::d_pushIndex` with full sequential
740 // consistency, which is required to ensure the visibility of the
741 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
742 // 2.
743
744 if (isFull() && isEnabled()) {
745 d_pushControlSema.wait();
746 }
747
748 d_numWaitingPushers.addRelaxed(-1);
749 }
750
751 return 0;
752}
753
754template <class TYPE>
756{
757 while (0 != tryPopFront(value)) {
758 d_numWaitingPoppers.addRelaxed(1);
759
760 // SYNCHRONIZATION POINT 2-Prime
761 //
762 // The following call to `isEmpty` loads
763 // `FixedQueueIndexManager::d_pushIndex` with full sequential
764 // consistency, which is required to ensure the visibility of the
765 // preceding change to `d_numWaitingPushers` to SYNCHRONIZATION POINT
766 // 2.
767
768 if (isEmpty()) {
769 d_popControlSema.wait();
770 }
771
772 d_numWaitingPoppers.addRelaxed(-1);
773 }
774}
775
776template <class TYPE>
778{
779 unsigned int generation;
780 unsigned int index;
781
782 while (0 != d_impl.reservePopIndex(&generation, &index)) {
783 d_numWaitingPoppers.addRelaxed(1);
784
785 if (isEmpty()) {
786 d_popControlSema.wait();
787 }
788
789 d_numWaitingPoppers.addRelaxed(-1);
790 }
791
792 // Copy the element. `FixedQueue_PopGuard` will destroy original object,
793 // update the queue, and release a waiting pusher, even if the copy
794 // constructor throws.
795
796 FixedQueue_PopGuard<TYPE> guard(this, generation, index);
797 // Unfortunately, in C++03, there are user types where a MovableRef will
798 // not safely degrade to a lvalue reference when a move constructor is not
799 // available, so `move` cannot be used directly on a user supplied type.
800 // See internal bug report 99039150.
801#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
802 return TYPE(bslmf::MovableRefUtil::move(d_elements[index]));
803#else
804 return TYPE(d_elements[index]);
805#endif
806}
807
808template <class TYPE>
810{
811 const int numItems = numElements();
812 int poppedItems = 0;
813 while (poppedItems++ < numItems) {
814 unsigned int index;
815 unsigned int generation;
816
817 if (0 != d_impl.reservePopIndex(&generation, &index)) {
818 break;
819 }
820
821 bslma::DestructionUtil::destroy(d_elements + index);
822 d_impl.commitPopIndex(generation, index);
823 }
824
825 int numWakeUps = bsl::min(poppedItems,
826 static_cast<int>(d_numWaitingPushers));
827 while (numWakeUps--) {
828 // Wake up waiting pushers.
829
830 d_pushControlSema.post();
831 }
832}
833
834template <class TYPE>
836{
837 d_impl.disable();
838
839 const int numWaitingPushers = d_numWaitingPushers;
840
841 for (int i = 0; i < numWaitingPushers; ++i) {
842 d_pushControlSema.post();
843 }
844}
845
846template <class TYPE>
847inline
849{
850 d_impl.enable();
851}
852
853// ACCESSORS
854template <class TYPE>
855inline
857{
858 return static_cast<int>(d_impl.capacity());
859}
860
861template <class TYPE>
862inline
864{
865 return (0 >= numElements());
866}
867
868template <class TYPE>
869inline
871{
872 return d_impl.isEnabled();
873}
874
875template <class TYPE>
876inline
878{
879 return (capacity() <= numElements());
880}
881
882template <class TYPE>
883inline
885{
886 return numElements();
887}
888
889template <class TYPE>
890inline
892{
893 return static_cast<int>(d_impl.length());
894}
895
896template <class TYPE>
897inline
899{
900 return static_cast<int>(capacity());
901}
902
903 // -------------------------
904 // class FixedQueue_PopGuard
905 // -------------------------
906
907// CREATORS
908template <class VALUE>
909inline
911 unsigned int generation,
912 unsigned int index)
913: d_parent_p(queue)
914, d_generation(generation)
915, d_index(index)
916{
917}
918
919template <class VALUE>
921{
922 // This popping thread currently has the cell at `d_index` (in
923 // `d_generation`) reserved for popping. Destroy the element at that
924 // position and then release the reservation. Wake up to 1 waiting pusher
925 // thread.
926
927 bslma::DestructionUtil::destroy(d_parent_p->d_elements + d_index);
928
929 d_parent_p->d_impl.commitPopIndex(d_generation, d_index);
930
931 // Notify pusher of available element.
932
934 d_parent_p->d_numWaitingPushers)) {
935 d_parent_p->d_pushControlSema.post();
936 }
937}
938
939 // ----------------------------
940 // class FixedQueue_PushProctor
941 // ----------------------------
942
943// CREATORS
944template <class VALUE>
945inline
947 FixedQueue<VALUE> *queue,
948 unsigned int generation,
949 unsigned int index)
950: d_parent_p(queue)
951, d_generation(generation)
952, d_index(index)
953{
954}
955
956template <class VALUE>
958{
959 if (d_parent_p) {
960 // This pushing thread currently has the cell at `d_index` reserved as
961 // `e_WRITING`. Dispose of all the elements up to `d_index`.
962
963 unsigned int generation, index;
964
965 // We will always have at least 1 popped item for the cell reserved for
966 // writing by the current thread.
967
968 int poppedItems = 1;
969 while (0 == d_parent_p->d_impl.reservePopIndexForClear(&generation,
970 &index,
971 d_generation,
972 d_index)) {
973 bslma::DestructionUtil::destroy(d_parent_p->d_elements + index);
974 ++poppedItems;
975
976 d_parent_p->d_impl.commitPopIndex(generation, index);
977 }
978
979 // Release the currently held pop index.
980
981 d_parent_p->d_impl.abortPushIndexReservation(d_generation, d_index);
982
983 while (poppedItems--) {
984 // Wake up waiting pushers.
985
986 d_parent_p->d_pushControlSema.post();
987 }
988 }
989}
990
991// MANIPULATORS
992template <class VALUE>
993inline
995{
996 d_parent_p = 0;
997}
998
999} // close package namespace
1000
1001
1002#endif
1003
1004// ----------------------------------------------------------------------------
1005// Copyright 2015 Bloomberg Finance L.P.
1006//
1007// Licensed under the Apache License, Version 2.0 (the "License");
1008// you may not use this file except in compliance with the License.
1009// You may obtain a copy of the License at
1010//
1011// http://www.apache.org/licenses/LICENSE-2.0
1012//
1013// Unless required by applicable law or agreed to in writing, software
1014// distributed under the License is distributed on an "AS IS" BASIS,
1015// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1016// See the License for the specific language governing permissions and
1017// limitations under the License.
1018// ----------------------------- END-OF-FILE ----------------------------------
1019
1020/** @} */
1021/** @} */
1022/** @} */
Definition bdlcc_fixedqueueindexmanager.h:257
Definition bdlcc_fixedqueue.h:442
~FixedQueue_PopGuard()
Definition bdlcc_fixedqueue.h:920
Definition bdlcc_fixedqueue.h:493
~FixedQueue_PushProctor()
Definition bdlcc_fixedqueue.h:957
void release()
Definition bdlcc_fixedqueue.h:994
Definition bdlcc_fixedqueue.h:274
~FixedQueue()
Destroy this object.
Definition bdlcc_fixedqueue.h:567
bool isFull() const
Definition bdlcc_fixedqueue.h:877
void removeAll()
Definition bdlcc_fixedqueue.h:809
bool isEnabled() const
Definition bdlcc_fixedqueue.h:870
int size() const
[DEPRECATED] Invoke capacity.
Definition bdlcc_fixedqueue.h:898
int capacity() const
Definition bdlcc_fixedqueue.h:856
int length() const
[DEPRECATED] Invoke numElements.
Definition bdlcc_fixedqueue.h:884
int pushBack(const TYPE &value)
Definition bdlcc_fixedqueue.h:693
void enable()
Definition bdlcc_fixedqueue.h:848
int numElements() const
Returns the number of elements currently in this queue.
Definition bdlcc_fixedqueue.h:891
int tryPushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_fixedqueue.h:614
TYPE popFront()
Definition bdlcc_fixedqueue.h:777
void disable()
Definition bdlcc_fixedqueue.h:835
bool isEmpty() const
Definition bdlcc_fixedqueue.h:863
BSLMF_NESTED_TRAIT_DECLARATION(FixedQueue, bslma::UsesBslmaAllocator)
int pushBack(bslmf::MovableRef< TYPE > value)
Definition bdlcc_fixedqueue.h:724
FixedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator=0)
Definition bdlcc_fixedqueue.h:549
int tryPushBack(const TYPE &value)
Definition bdlcc_fixedqueue.h:574
int tryPopFront(TYPE *value)
Definition bdlcc_fixedqueue.h:655
void popFront(TYPE *value)
Definition bdlcc_fixedqueue.h:755
Definition bslma_allocator.h:457
virtual void * allocate(size_type size)=0
Definition bslmf_movableref.h:751
Definition bslmt_semaphore.h:168
Definition bsls_atomic.h:743
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(expr)
Definition bsls_performancehint.h:452
Definition bdlcc_boundedqueue.h:270
Definition balxml_encoderoptions.h:68
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1642
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1599
Definition bslma_usesbslmaallocator.h:343
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1060
@ e_CACHE_LINE_SIZE
Definition bslmt_platform.h:213