// bdlcc_fixedqueue.h                                                 -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLCC_FIXEDQUEUE
#define INCLUDED_BDLCC_FIXEDQUEUE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a thread-aware fixed-size queue of values.
//
//@CLASSES:
//  bdlcc::FixedQueue: thread-aware fixed-size queue of 'TYPE' values
//
//@DESCRIPTION: This component defines a type, 'bdlcc::FixedQueue', that
// provides an efficient, thread-aware fixed-size queue of values.  This
// class is ideal for synchronization and communication between threads in a
// producer-consumer model.  Under most cicrumstances developers should prefer
// the newer {bdlcc_boundedqueue} (see {Comparison to BoundedQueue}).
//
// The queue provides 'pushBack' and 'popFront' methods for pushing data into
// the queue and popping it from the queue.  In case of overflow (queue full
// when pushing), or underflow (queue empty when popping), the methods block
// until data or free space in the queue appears.  Non-blocking methods
// 'tryPushBack' and 'tryPopFront' are also provided, which fail immediately
// returning a non-zero value in case of overflow or underflow.
//
// The queue may be placed into a "disabled" state using the 'disable' method.
// When disabled, 'pushBack' and 'tryPushBack' fail immediately (they do not
// block and any blocked invocations will fail immediately).  The queue may be
// restored to normal operation with the 'enable' method.
//
// Unlike 'bdlcc::Queue', a fixed queue is not double-ended, there is no timed
// API like 'timedPushBack' and 'timedPopFront', and no 'forcePush' methods, as
// the queue capacity is fixed.  Also, this component is not based on
// 'bdlc::Queue', so there is no API for direct access to the underlying queue.
// These limitations are a trade-off for significant gain in performance
// compared to 'bdlcc::Queue'.
//
///Comparison To BoundedQueue
///--------------------------
// Both 'bdlcc::FixedQueue' and 'bdlcc::BoundedQueue' provide thread-aware
// bounded queues.  Under most circumstances developers should prefer
// {bdlcc_boundedqueue}: it is newer, has additional features, and provides
// better performance under most circumstances.  'bdlcc::BoundedQueue' is not
// quite a drop in replacement for 'bdlcc::FixedQueue' so both types are
// currently maintained.  There is additional information about
// performance of various queues in the article Concurrent Queue Evaluation
// (https://tinyurl.com/mr2un9f7).
//
///Template Requirements
///---------------------
// 'bdlcc::FixedQueue' is a template that is parameterized on the type of
// element contained within the queue.  The supplied template argument, 'TYPE',
// must provide both a default constructor and a copy constructors as well as
// an assignment operator.  If the default constructor accepts a
// 'bslma::Allocator*', 'TYPE' must declare the uses 'bslma::Allocator' trait
// (see 'bslma_usesbslmaallocator') so that the allocator of the queue is
// propagated to the elements contained in the queue.
//
///Exception safety
///----------------
// A 'bdlcc::FixedQueue' is exception neutral, and all of the methods of
// 'bdlcc::FixedQueue' provide the strong exception safety guarantee except for
// 'pushBack' and 'tryPushBack', which provide the basic exception guarantee
// (see 'bsldoc_glossary').
//
///Memory Usage
///------------
// 'bdlcc::FixedQueue' is most efficient when dealing with small objects or
// fundamental types (as a thread-safe container, its methods pass objects *by*
// *value*).  We recommend:
//: o Large objects be stored as shared-pointers (or possibly raw pointers).
//: o Clients take care in specifying the queue capacity (specified in a number
//:   of objects, *not* a number of bytes).
//
// Note that the implementation of 'bdlcc::FixedQueue' currently creates a
// fixed size array of the contained object type.
//
///Move Semantics in C++03
///-----------------------
// Move-only types are supported by 'FixedQueue' on C++11 platforms only (where
// 'BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES' is defined), and are not supported
// on C++03 platforms.  Unfortunately, in C++03, there are user types where a
// 'bslmf::MovableRef' will not safely degrade to a lvalue reference when a
// move constructor is not available (types providing a constructor template
// taking any type), so 'bslmf::MovableRefUtil::move' cannot be used directly
// on a user supplied template type. See internal bug report 99039150 for more
// information.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: A Simple Thread Pool
///- - - - - - - - - - - - - - - -
// In the following example a 'bdlcc::FixedQueue' is used to communicate
// between a single "producer" thread and multiple "consumer" threads.  The
// "producer" will push work requests onto the queue, and each "consumer" will
// iteratively take a work request from the queue and service the request.
// This example shows a partial, simplified implementation of the
// 'bdlmt::FixedThreadPool' class.  See component 'bdlmt_fixedthreadpool' for
// more information.
//
// First, we define a utility classes that handles a simple "work item":
//..
//  struct my_WorkData {
//      // Work data...
//  };
//
//  struct my_WorkRequest {
//      enum RequestType {
//          e_WORK = 1,
//          e_STOP = 2
//      };
//
//      RequestType d_type;
//      my_WorkData d_data;
//      // Work data...
//  };
//..
// Next, we provide a simple function to service an individual work item.  The
// details are unimportant for this example:
//..
//  void myDoWork(my_WorkData& data)
//  {
//      // do some stuff...
//      (void)data;
//  }
//..
// Then, we define a 'myConsumer' function that will pop elements off the queue
// and process them.  Note that the call to 'queue->popFront()' will block
// until there is an element available on the queue.  This function will be
// executed in multiple threads, so that each thread waits in
// 'queue->popFront()', and 'bdlcc::FixedQueue' guarantees that each thread
// gets a unique element from the queue:
//..
//  void myConsumer(bdlcc::FixedQueue<my_WorkRequest> *queue)
//  {
//      while (1) {
//          // 'popFront()' will wait for a 'my_WorkRequest' until available.
//
//          my_WorkRequest item = queue->popFront();
//          if (item.d_type == my_WorkRequest::e_STOP) { break; }
//          myDoWork(item.d_data);
//      }
//  }
//..
// Finally, we define a 'myProducer' function that serves multiple roles: it
// creates the 'bdlcc::FixedQueue', starts the consumer threads, and then
// produces and enqueues work items.  When work requests are exhausted, this
// function enqueues one 'e_STOP' item for each consumer queue.  This 'e_STOP'
// item indicates to the consumer thread to terminate its thread-handling
// function.
//
// Note that, although the producer cannot control which thread 'pop's a
// particular work item, it can rely on the knowledge that each consumer thread
// will read a single 'e_STOP' item and then terminate.
//..
//  void myProducer(int numThreads)
//  {
//      enum {
//          k_MAX_QUEUE_LENGTH = 100,
//          k_NUM_WORK_ITEMS   = 1000
//      };
//
//      bdlcc::FixedQueue<my_WorkRequest> queue(k_MAX_QUEUE_LENGTH);
//
//      bslmt::ThreadGroup consumerThreads;
//      consumerThreads.addThreads(bdlf::BindUtil::bind(&myConsumer, &queue),
//                                 numThreads);
//
//      for (int i = 0; i < k_NUM_WORK_ITEMS; ++i) {
//          my_WorkRequest item;
//          item.d_type = my_WorkRequest::e_WORK;
//          item.d_data = my_WorkData(); // some stuff to do
//          queue.pushBack(item);
//      }
//
//      for (int i = 0; i < numThreads; ++i) {
//          my_WorkRequest item;
//          item.d_type = my_WorkRequest::e_STOP;
//          queue.pushBack(item);
//      }
//
//      consumerThreads.joinAll();
//  }
//..

#include <bdlscm_version.h>

#include <bdlcc_fixedqueueindexmanager.h>

#include <bslmt_semaphore.h>
#include <bslmt_threadutil.h>

#include <bsls_atomic.h>

#include <bslalg_scalarprimitives.h>

#include <bslma_default.h>
#include <bslma_destructionutil.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_platform.h>
#include <bsls_performancehint.h>
#include <bsls_types.h>

#include <bsl_algorithm.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace bdlcc {

                              // ================
                              // class FixedQueue
                              // ================

template <class TYPE>
class FixedQueue {
    // This class provides a thread-aware, lock-free, fixed-size queue of
    // values.

  private:

    // PRIVATE CONSTANTS
    enum {
        k_TYPE_PADDING = bslmt::Platform::e_CACHE_LINE_SIZE - sizeof(TYPE *),
        k_SEMA_PADDING = bslmt::Platform::e_CACHE_LINE_SIZE -
                                                       sizeof(bslmt::Semaphore)
    };

    // DATA
    TYPE             *d_elements;          // array of elements that comprise
                                           // the fixed queue (array elements
                                           // are manually constructed and
                                           // destroyed, and empty elements
                                           // hold uninitialized memory)

    const char        d_elementsPad[k_TYPE_PADDING];
                                           // padding to prevent false sharing
    FixedQueueIndexManager
                      d_impl;              // index manager for managing the
                                           // state of 'd_elements'

    bsls::AtomicInt   d_numWaitingPoppers; // number of threads waiting on
                                           // 'd_popControlSema' to pop an
                                           // element

    bslmt::Semaphore  d_popControlSema;    // semaphore on which threads
                                           // waiting to pop 'wait'

    const char        d_popControlSemaPad[k_SEMA_PADDING];
                                           // padding to prevent false sharing

    bsls::AtomicInt   d_numWaitingPushers; // number of threads waiting on
                                           // 'd_pushControlSema' to push an
                                           // element

    bslmt::Semaphore  d_pushControlSema;   // semaphore on which threads
                                           // waiting to push 'wait'

    const char        d_pushControlSemaPad[k_SEMA_PADDING];
                                           // padding to prevent false sharing

    bslma::Allocator *d_allocator_p;       // allocator, held not owned

  private:
    // NOT IMPLEMENTED
    FixedQueue(const FixedQueue&);
    FixedQueue& operator=(const FixedQueue&);

    // FRIENDS
    template <class VAL> friend class FixedQueue_PushProctor;
    template <class VAL> friend class FixedQueue_PopGuard;

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(FixedQueue, bslma::UsesBslmaAllocator);
    // CREATORS
    explicit
    FixedQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
        // Create a thread-aware lock-free queue having the specified
        // 'capacity'.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.  The behavior is undefined unless '0 < capacity'
        // and 'capacity <= bdlcc::FixedQueueIndexManager::k_MAX_CAPACITY'.

    ~FixedQueue();
        // Destroy this object.

    // MANIPULATORS
    int pushBack(const TYPE& value);
        // Append the specified 'value' to the back of this queue, blocking
        // until either space is available - if necessary - or the queue is
        // disabled.  Return 0 on success, and a nonzero value if the queue is
        // disabled.

    int pushBack(bslmf::MovableRef<TYPE> value);
        // Append the specified move-insertable 'value' to the back of this
        // queue, blocking until either space is available - if necessary - or
        // the queue is disabled.  'value' is left in a valid but unspecified
        // state.  Return 0 on success, and a nonzero value if the queue is
        // disabled.

    int tryPushBack(const TYPE& value);
        // Attempt to append the specified 'value' to the back of this queue
        // without blocking.  Return 0 on success, and a non-zero value if the
        // queue is full or disabled.

    int tryPushBack(bslmf::MovableRef<TYPE> value);
        // Attempt to append the specified move-insertable 'value' to the back
        // of this queue without blocking.  'value' is left in a valid but
        // unspecified state.  Return 0 on success, and a non-zero value if the
        // queue is full or disabled.

    void popFront(TYPE* value);
        // Remove the element from the front of this queue and load that
        // element into the specified 'value'.  If the queue is empty, block
        // until it is not empty.

    TYPE popFront();
        // Remove the element from the front of this queue and return it's
        // value.  If the queue is empty, block until it is not empty.

    int tryPopFront(TYPE *value);
        // Attempt to remove the element from the front of this queue without
        // blocking, and, if successful, load the specified 'value' with the
        // removed element.  Return 0 on success, and a non-zero value if queue
        // was empty.  On failure, 'value' is not changed.

    void removeAll();
        // Remove all items from this queue.  Note that this operation is not
        // atomic; if other threads are concurrently pushing items into the
        // queue the result of numElements() after this function returns is not
        // guaranteed to be 0.

    void disable();
        // Disable this queue.  All subsequent invocations of 'pushBack' or
        // 'tryPushBack' will fail immediately.  All blocked invocations of
        // 'pushBack' will fail immediately.  If the queue is already disabled,
        // this method has no effect.

    void enable();
        // Enable queuing.  If the queue is not disabled, this call has no
        // effect.

    // ACCESSORS
    int capacity() const;
        // Return the maximum number of elements that may be stored in this
        // queue.

    bool isEmpty() const;
        // Return 'true' if this queue is empty (has no elements), or 'false'
        // otherwise.

    bool isEnabled() const;
        // Return 'true' if this queue is enabled, and 'false' otherwise.  Note
        // that the queue is created in the "enabled" state.

    bool isFull() const;
        // Return 'true' if this queue is full (when the number of elements
        // currently in this queue equals its capacity), or 'false' otherwise.

    int numElements() const;
        // Returns the number of elements currently in this queue.

    int length() const;
        // [!DEPRECATED!] Invoke 'numElements'.

    int size() const;
        // [!DEPRECATED!] Invoke 'capacity'.

};

                         // =========================
                         // class FixedQueue_PopGuard
                         // =========================

template <class VALUE>
class FixedQueue_PopGuard {
    // This class provides a guard that, upon its destruction, will remove
    // (pop) the indicated element from the 'FixedQueue' object supplied at
    // construction.  Note that this guard is used to provide exception safety
    // when popping an element from a 'FixedQueue' object.

    // DATA
    FixedQueue<VALUE> *d_parent_p;
                                     // object from which an element will be
                                     // popped

    unsigned int                  d_generation;
                                     // generation count of cell being popped

    unsigned int                  d_index;
                                     // index of cell being popped

  private:
    // NOT IMPLEMENTED
    FixedQueue_PopGuard(const FixedQueue_PopGuard&);
    FixedQueue_PopGuard& operator=(const FixedQueue_PopGuard&);
  public:

    // CREATORS
    FixedQueue_PopGuard(FixedQueue<VALUE> *queue,
                        unsigned int       generation,
                        unsigned int       index);
        // Create a guard that, upon its destruction, will update the state of
        // the specified 'queue' to remove (pop) the element at the specified
        // 'index' having the specified 'generation', and destroy that popped
        // object.  The behavior is undefined unless 'index' and 'generation'
        // refer to a valid element in 'queue' that the current thread has
        // acquired a reservation to pop (using
        // 'FixedQueueIndexManager::reservePopIndex').

    ~FixedQueue_PopGuard();
        // Update the state of the 'FixedQueue' object supplied at construction
        // to remove (pop) the indicated element, and destroy the popped
        // object.
};

                        // ============================
                        // class FixedQueue_PushProctor
                        // ============================

template <class VALUE>
class FixedQueue_PushProctor {
    // This class provides a proctor that, unless the 'release' method has been
    // previously invoked, will remove and destroy all the elements from a
    // 'FixedQueue' object supplied at construction (putting that ring-buffer
    // into a valid empty state) upon the proctor's destruction.  Note that
    // this guard is used to provide exception safety when pushing an element
    // into a 'FixedQueue'.

    // DATA
    FixedQueue<VALUE> *d_parent_p;
                                     // object in which an element was pushed

    unsigned int                  d_generation;
                                     // generation of cell being pushed when an
                                     // exception was thrown

    unsigned int                  d_index;
                                     // index of cell being pushed when an
                                     // exception was thrown

  private:
    // NOT IMPLEMENTED
    FixedQueue_PushProctor(const FixedQueue_PushProctor&);
    FixedQueue_PushProctor& operator=(const FixedQueue_PushProctor&);

  public:

    // CREATORS
    FixedQueue_PushProctor(FixedQueue<VALUE> *queue,
                           unsigned int       generation,
                           unsigned int       index);
        // Create a proctor that manages the specified 'queue' and, unless
        // 'release' is called, will remove and destroy all the elements from
        // 'queue' starting at the specified 'index' in the specified
        // 'generation'.  The behavior is undefined unless 'index' and
        // 'generation' refers to a valid element in 'queue'.

    ~FixedQueue_PushProctor();
        // Destroy this proctor and, if 'release' was not called on this
        // object, remove and destroy all the elements from the 'FixedQueue'
        // object supplied at construction.

    // MANIPULATORS
    void release();
        // Release from management the 'FixedQueue' object supplied at
        // construction.

};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

// See the .cpp for an implementation note.

                           // ---------------------
                           // class FixedQueue
                           // ---------------------
// CREATORS
template <class TYPE>
FixedQueue<TYPE>::FixedQueue(bsl::size_t       capacity,
                             bslma::Allocator *basicAllocator)
: d_elements()
, d_elementsPad()
, d_impl(capacity, basicAllocator)
, d_numWaitingPoppers(0)
, d_popControlSema(0)
, d_popControlSemaPad()
, d_numWaitingPushers(0)
, d_pushControlSema(0)
, d_pushControlSemaPad()
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_elements = static_cast<TYPE *>(
                            d_allocator_p->allocate(capacity * sizeof(TYPE)));
}

template <class TYPE>
FixedQueue<TYPE>::~FixedQueue()
{
    removeAll();
    d_allocator_p->deallocate(d_elements);
}

template <class TYPE>
int FixedQueue<TYPE>::tryPushBack(const TYPE& value)
{
    unsigned int generation;
    unsigned int index;

    // SYNCHRONIZATION POINT 1
    //
    // The following call to 'reservePushIndex' writes
    // 'FixedQueueIndexManaged::d_pushIndex' with full sequential consistency,
    // which guarantees the subsequent (relaxed) read from
    // 'd_numWaitingPoppers' sees any waiting poppers from SYNCHRONIZATION
    // POINT 1-Prime.

    int retval = d_impl.reservePushIndex(&generation, &index);

    if (0 != retval) {
        return retval;                                                // RETURN
    }

    // Copy the element into the cell.  If an exception is thrown by the copy
    // constructor, PushProctor will pop and discard items until reaching this
    // cell, then mark this cell empty (without regard to its current state,
    // which is WRITING (i.e., reserved)).  That will leave the queue in a
    // valid empty state.

    FixedQueue_PushProctor<TYPE> guard(this, generation, index);
    bslalg::ScalarPrimitives::copyConstruct(&d_elements[index],
                                            value,
                                            d_allocator_p);
    guard.release();
    d_impl.commitPushIndex(generation, index);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
        d_popControlSema.post();
    }

    return 0;
}

template <class TYPE>
int FixedQueue<TYPE>::tryPushBack(bslmf::MovableRef<TYPE> value)
{
    unsigned int generation;
    unsigned int index;

    // SYNCHRONIZATION POINT 1
    //
    // The following call to 'reservePushIndex' writes
    // 'FixedQueueIndexManaged::d_pushIndex' with full sequential consistency,
    // which guarantees the subsequent (relaxed) read from
    // 'd_numWaitingPoppers' sees any waiting poppers from SYNCHRONIZATION
    // POINT 1-Prime.

    int retval = d_impl.reservePushIndex(&generation, &index);

    if (0 != retval) {
        return retval;                                                // RETURN
    }

    // Move the element into the cell.  If an exception is thrown by the move
    // constructor, PushProctor will pop and discard items until reaching this
    // cell, then mark this cell empty (without regard to its current state,
    // which is WRITING (i.e., reserved)).  That will leave the queue in a
    // valid empty state.

    FixedQueue_PushProctor<TYPE> guard(this, generation, index);
    TYPE& dummy = value;
    bslalg::ScalarPrimitives::moveConstruct(&d_elements[index],
                                            dummy,
                                            d_allocator_p);
    guard.release();
    d_impl.commitPushIndex(generation, index);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_numWaitingPoppers)) {
        d_popControlSema.post();
    }

    return 0;
}

template <class TYPE>
int FixedQueue<TYPE>::tryPopFront(TYPE *value)
{
    unsigned int generation;
    unsigned int index;

    // SYNCHRONIZATION POINT 2
    //
    // The following call to 'reservePopIndex' writes
    // 'FixedQueueIndexManaged::d_popIndex' with full sequential consistency,
    // which guarantees the subsequent (relaxed) read from
    // 'd_numWaitingPoppers' sees any waiting poppers from SYNCHRONIZATION
    // POINT 2-Prime.

    int retval = d_impl.reservePopIndex(&generation, &index);

    if (0 != retval) {
        return retval;                                                // RETURN
    }

    // Copy or move the element.  'FixedQueue_PopGuard' will destroy original
    // object, update the queue, and release a waiting pusher, even if the
    // assignment operator throws.

    FixedQueue_PopGuard<TYPE> guard(this, generation, index);
    // Unfortunately, in C++03, there are user types where a MovableRef will
    // not safely degrade to a lvalue reference when a move constructor is not
    // available, so 'move' cannot be used directly on a user supplied type.
    // See internal bug report 99039150.
#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
    *value = bslmf::MovableRefUtil::move(d_elements[index]);
#else
    *value = d_elements[index];
#endif
    return 0;
}

// MANIPULATORS
template <class TYPE>
int FixedQueue<TYPE>::pushBack(const TYPE& value)
{
    int retval;
    while (0 != (retval = tryPushBack(value))) {
        if (retval < 0) {
            // The queue is disabled.

            return retval;                                            // RETURN
        }

        d_numWaitingPushers.addRelaxed(1);

        // SYNCHRONIZATION POINT 1-Prime
        //
        // The following call to 'isFull' loads
        // 'FixedQueueIndexManager::d_pushIndex' with full sequential
        // consistency, which is required to ensure the visibility of the
        // preceding change to 'd_numWaitingPushers' to SYNCHRONIZATION POINT
        // 2.

        if (isFull() && isEnabled()) {
            d_pushControlSema.wait();
        }

        d_numWaitingPushers.addRelaxed(-1);
    }

    return 0;
}

template <class TYPE>
int FixedQueue<TYPE>::pushBack(bslmf::MovableRef<TYPE> value)
{
    int retval;
    while (0 != (retval = tryPushBack(bslmf::MovableRefUtil::move(value)))) {
        if (retval < 0) {
            // The queue is disabled.

            return retval;                                            // RETURN
        }

        d_numWaitingPushers.addRelaxed(1);

        // SYNCHRONIZATION POINT 1-Prime
        //
        // The following call to 'isFull' loads
        // 'FixedQueueIndexManager::d_pushIndex' with full sequential
        // consistency, which is required to ensure the visibility of the
        // preceding change to 'd_numWaitingPushers' to SYNCHRONIZATION POINT
        // 2.

        if (isFull() && isEnabled()) {
            d_pushControlSema.wait();
        }

        d_numWaitingPushers.addRelaxed(-1);
    }

    return 0;
}

template <class TYPE>
void FixedQueue<TYPE>::popFront(TYPE *value)
{
    while (0 != tryPopFront(value)) {
        d_numWaitingPoppers.addRelaxed(1);

        // SYNCHRONIZATION POINT 2-Prime
        //
        // The following call to 'isEmpty' loads
        // 'FixedQueueIndexManager::d_pushIndex' with full sequential
        // consistency, which is required to ensure the visibility of the
        // preceding change to 'd_numWaitingPushers' to SYNCHRONIZATION POINT
        // 2.

        if (isEmpty()) {
            d_popControlSema.wait();
        }

        d_numWaitingPoppers.addRelaxed(-1);
    }
}

template <class TYPE>
TYPE FixedQueue<TYPE>::popFront()
{
    unsigned int generation;
    unsigned int index;

    while (0 != d_impl.reservePopIndex(&generation, &index)) {
        d_numWaitingPoppers.addRelaxed(1);

        if (isEmpty()) {
            d_popControlSema.wait();
        }

        d_numWaitingPoppers.addRelaxed(-1);
    }

    // Copy the element.  'FixedQueue_PopGuard' will destroy original object,
    // update the queue, and release a waiting pusher, even if the copy
    // constructor throws.

    FixedQueue_PopGuard<TYPE> guard(this, generation, index);
    // Unfortunately, in C++03, there are user types where a MovableRef will
    // not safely degrade to a lvalue reference when a move constructor is not
    // available, so 'move' cannot be used directly on a user supplied type.
    // See internal bug report 99039150.
#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
    return TYPE(bslmf::MovableRefUtil::move(d_elements[index]));
#else
    return TYPE(d_elements[index]);
#endif
}

template <class TYPE>
void FixedQueue<TYPE>::removeAll()
{
    const int numItems = numElements();
    int poppedItems = 0;
    while (poppedItems++ < numItems) {
        unsigned int index;
        unsigned int generation;

        if (0 != d_impl.reservePopIndex(&generation, &index)) {
            break;
        }

        bslma::DestructionUtil::destroy(d_elements + index);
        d_impl.commitPopIndex(generation, index);
    }

    int numWakeUps = bsl::min(poppedItems,
                              static_cast<int>(d_numWaitingPushers));
    while (numWakeUps--) {
        // Wake up waiting pushers.

        d_pushControlSema.post();
    }
}

template <class TYPE>
void FixedQueue<TYPE>::disable()
{
    d_impl.disable();

    const int numWaitingPushers = d_numWaitingPushers;

    for (int i = 0; i < numWaitingPushers; ++i) {
        d_pushControlSema.post();
    }
}

template <class TYPE>
inline
void FixedQueue<TYPE>::enable()
{
    d_impl.enable();
}

// ACCESSORS
template <class TYPE>
inline
int FixedQueue<TYPE>::capacity() const
{
    return static_cast<int>(d_impl.capacity());
}

template <class TYPE>
inline
bool FixedQueue<TYPE>::isEmpty() const
{
    return (0 >= numElements());
}

template <class TYPE>
inline
bool FixedQueue<TYPE>::isEnabled() const
{
    return d_impl.isEnabled();
}

template <class TYPE>
inline
bool FixedQueue<TYPE>::isFull() const
{
    return (capacity() <= numElements());
}

template <class TYPE>
inline
int FixedQueue<TYPE>::length() const
{
    return numElements();
}

template <class TYPE>
inline
int FixedQueue<TYPE>::numElements() const
{
    return static_cast<int>(d_impl.length());
}

template <class TYPE>
inline
int FixedQueue<TYPE>::size() const
{
    return static_cast<int>(capacity());
}

                         // -------------------------
                         // class FixedQueue_PopGuard
                         // -------------------------

// CREATORS
template <class VALUE>
inline
FixedQueue_PopGuard<VALUE>::FixedQueue_PopGuard(FixedQueue<VALUE> *queue,
                                                unsigned int       generation,
                                                unsigned int       index)
: d_parent_p(queue)
, d_generation(generation)
, d_index(index)
{
}

template <class VALUE>
FixedQueue_PopGuard<VALUE>::~FixedQueue_PopGuard()
{
    // This popping thread currently has the cell at 'd_index' (in
    // 'd_generation') reserved for popping.  Destroy the element at that
    // position and then release the reservation.  Wake up to 1 waiting pusher
    // thread.

    bslma::DestructionUtil::destroy(d_parent_p->d_elements + d_index);

    d_parent_p->d_impl.commitPopIndex(d_generation, d_index);

    // Notify pusher of available element.

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
            d_parent_p->d_numWaitingPushers)) {
        d_parent_p->d_pushControlSema.post();
    }
}

                        // ----------------------------
                        // class FixedQueue_PushProctor
                        // ----------------------------

// CREATORS
template <class VALUE>
inline
FixedQueue_PushProctor<VALUE>::FixedQueue_PushProctor(
                                                 FixedQueue<VALUE> *queue,
                                                 unsigned int       generation,
                                                 unsigned int       index)
: d_parent_p(queue)
, d_generation(generation)
, d_index(index)
{
}

template <class VALUE>
FixedQueue_PushProctor<VALUE>::~FixedQueue_PushProctor()
{
    if (d_parent_p) {
        // This pushing thread currently has the cell at 'd_index' reserved as
        // 'e_WRITING'.  Dispose of all the elements up to 'd_index'.

        unsigned int generation, index;

        // We will always have at least 1 popped item for the cell reserved for
        // writing by the current thread.

        int poppedItems = 1;
        while (0 == d_parent_p->d_impl.reservePopIndexForClear(&generation,
                                                               &index,
                                                               d_generation,
                                                               d_index)) {
            bslma::DestructionUtil::destroy(d_parent_p->d_elements + index);
            ++poppedItems;

            d_parent_p->d_impl.commitPopIndex(generation, index);
        }

        // Release the currently held pop index.

        d_parent_p->d_impl.abortPushIndexReservation(d_generation, d_index);

        while (poppedItems--) {
            // Wake up waiting pushers.

            d_parent_p->d_pushControlSema.post();
        }
    }
}

// MANIPULATORS
template <class VALUE>
inline
void FixedQueue_PushProctor<VALUE>::release()
{
    d_parent_p = 0;
}
}  // close package namespace

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------