// bdlcc_queue.h                                                      -*-C++-*-
#ifndef INCLUDED_BDLCC_QUEUE
#define INCLUDED_BDLCC_QUEUE

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

//@PURPOSE: Provide a thread-enabled queue of items of parameterized `TYPE`.
//
//@CLASSES:
//   bdlcc::Queue: thread-enabled `bdlc::Queue` wrapper
//
//@SEE_ALSO: bdlc_queue
//
//@DEPRECATED: use `bdlcc::Deque` instead.
//
//@DESCRIPTION: This component provides a thread-enabled implementation of an
// efficient, in-place, indexable, double-ended queue of parameterized `TYPE`
// values, namely the `bdlcc::Queue<TYPE>` container.  `bdlcc::Queue` is
// effectively a thread-enabled handle for `bdlc::Queue`, whose interface is
// also made available through `bdlcc::Queue`.
//
///Thread-Enabled Idioms in the `bdlcc::Queue` Interface
///-----------------------------------------------------
// The thread-enabled `bdlcc::Queue` is similar to `bdlc::Queue` in many
// regards, but there are several differences in method behavior and signature
// that arise due to the thread-enabled nature of the queue and its anticipated
// usage pattern.  Most notably, the `popFront` and `popBack` methods return a
// `TYPE` object *by* *value*, rather than returning `void`, as `bdlc::Queue`
// does.  Moreover, if a queue object is empty, `popFront` and `popBack` will
// block indefinitely until an item is added to the queue.
//
// As a corollary to this behavior choice, `bdlcc::Queue` also provides
// `timedPopFront` and `timedPopBack` methods.  These methods wait until a
// specified timeout expires if the queue is empty, returning an item if one
// becomes available before the specified timeout; otherwise, they return a
// non-zero value to indicate that the specified timeout expired before an item
// was available.  Note that *all* timeouts are expressed as values of type
// `bsls::TimeInterval` that represent **ABSOLUTE** times from 00:00:00 UTC,
// January 1, 1970.
//
// The behavior of the `push` methods differs in a similar manner.
// `bdlcc::Queue` supports the notion of a suggested maximum queue size, called
// the "high-water mark", a value supplied at construction.  The `pushFront`
// and `pushBack` methods will block indefinitely if the queue contains (at
// least) the high-water mark number of items, until the number of items falls
// below the high-water mark.  The `timedPushFront` and `timedPushBack` are
// provided to limit the duration of blocking; note, however, that these
// methods can fail to add an item to the queue.  For this reason,
// `bdlcc::Queue` also provides a `forcePushFront` method that will override
// the high-water mark, if needed, in order to succeed without blocking.  Note
// that this design decision makes the high-water mark concept a suggestion and
// not an invariant.
//
///Use of the `bdlc::Queue` Interface
///----------------------------------
// Class `bdlcc::Queue` provides access to an underlying `bdlc::Queue`, so
// clients of `bdlcc::Queue` have full access to the interface behavior of
// `bdlc::Queue` to inspect and modify the `bdlcc::Queue`.
//
// Member function `bdlcc::Queue::queue()` provides *direct* modifiable access
// to the `bdlc::Queue` object used in the implementation.  Member functions
// `bdlcc::Queue::mutex()`, `bdlcc::Queue::notEmptyCondition()`, and
// `bdlcc::Queue::notFullCondition()` correspondingly provide *direct*
// modifiable access to the underlying `bslmt::Mutex` and `bslmt::Condition`
// objects respectively.  These underlying objects are used within
// `bdlcc::Queue` to manage concurrent access to the queue.  Clients may use
// these member variables together if needed.
//
// Whenever accessing the `bdlcc` queue directly, clients must be sure to lock
// and unlock the mutex or to signal or broadcast on the condition variable as
// appropriate.  For example, a client might use the underlying queue and mutex
// as follows:
// ```
//    bdlcc::Queue<myData>  myWorkQueue;
//    bdlc::Queue<myData>& rawQueue = myWorkQueue.queue();
//    bslmt::Mutex&        queueMutex = myWorkQueue.mutex();
//        // other code omitted...
//
//    myData  data1;
//    myData  data2;
//    bool pairFoundFlag = 0;
//    // Take two items from the queue atomically, if available.
//
//    queueMutex.lock();
//    if (rawQueue.length() >= 2) {
//        data1 = rawQueue.front();
//        rawQueue.popFront();
//        data2 = rawQueue.front();
//        rawQueue.popFront();
//        pairFound = 1;
//    }
//    queueMutex.unlock();
//
//    if (pairFoundFlag) {
//        // Process the pair
//    }
// ```
// Note that a future version of this component will provide access to a
// thread-safe "smart pointer" that will manage the `bdlc::Queue` with respect
// to locking and signaling.  At that time, direct access to the `bdlc::Queue`
// will be deprecated.  In the meanwhile, the user should be careful to use the
// `bdlc::Queue` and the synchronization objects properly.
//
///WARNING: Synchronization Required on Destruction
///------------------------------------------------
// The behavior for the destructor is undefined unless all access or
// modification of the object is completed prior to its destruction.  Some form
// of synchronization, external to the component, is required to ensure the
// precondition on the destructor is met.  For example, if two (or more)
// threads are manipulating a queue, it is *not* safe to anticipate the number
// of elements added to the queue, and destroy that queue immediately after the
// last element is popped (without additional synchronization) because one of
// the corresponding push functions may not have completed (push may, for
// instance, signal waiting threads after the element is considered added to
// the queue).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Simple Thread Pool
///- - - - - - - - - - - - - - -
// The following example demonstrates a typical usage of a `bdlcc::Queue`.
//
// This `bdlcc::Queue` 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::ThreadPool` class.  See
// component `bdlmt_threadpool` for more information.
//
// We begin our example with some utility classes that define a simple "work
// item":
// ```
// enum {
//     k_MAX_CONSUMER_THREADS = 10
// };
//
// 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;
// }
// ```
// The `myConsumer` function will pop items off the queue and process them.  As
// discussed above, note that the call to `queue->popFront()` will block until
// there is an item available on the queue.  This function will be executed in
// multiple threads, so that each thread waits in `queue->popFront()`, and
// `bdlcc::Queue` guarantees that each thread gets a unique item from the
// queue.
// ```
// void myConsumer(bdlcc::Queue<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);
//     }
// }
// ```
// The function below is a callback for `bslmt::ThreadUtil`, which requires a
// "C" signature.  `bslmt::ThreadUtil::create()` expects a pointer to this
// function, and provides that function pointer to the newly created thread.
// The new thread then executes this function.
//
// Since `bslmt::ThreadUtil::create()` uses the familiar "C" convention of
// passing a `void` pointer, our function simply casts that pointer to our
// required type (`bdlcc::Queue<my_WorkRequest*> *`), and then delegates to the
// queue-specific function `myConsumer`, above.
// ```
// extern "C" void *myConsumerThread(void *queuePtr)
// {
//     myConsumer ((bdlcc::Queue<my_WorkRequest> *)queuePtr);
//     return queuePtr;
// }
// ```
// In this simple example, the `myProducer` function serves multiple roles: it
// creates the `bdlcc::Queue`, starts out the consumer threads, and then
// produces and queues work items.  When work requests are exhausted, this
// function queues one `STOP` item for each consumer queue.
//
// When each Consumer thread reads a `STOP`, it terminates 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 `STOP` item and then terminate.
//
// Finally, the `myProducer` function "joins" each Consumer thread, which
// ensures that the thread itself will terminate correctly; see the
// `bslmt_threadutil` component for details.
// ```
// void myProducer(int numThreads)
// {
//     my_WorkRequest item;
//     my_WorkData    workData;
//
//     bdlcc::Queue<my_WorkRequest> queue;
//
//     assert(0 < numThreads && numThreads <= k_MAX_CONSUMER_THREADS);
//     bslmt::ThreadUtil::Handle consumerHandles[k_MAX_CONSUMER_THREADS];
//
//     for (int i = 0; i < numThreads; ++i) {
//         bslmt::ThreadUtil::create(&consumerHandles[i],
//                                   myConsumerThread,
//                                   &queue);
//     }
//
//     while (!getWorkData(&workData)) {
//         item.d_type = my_WorkRequest::e_WORK;
//         item.d_data = workData;
//         queue.pushBack(item);
//     }
//
//     for (int i = 0; i < numThreads; ++i) {
//         item.d_type = my_WorkRequest::e_STOP;
//         queue.pushBack(item);
//     }
//
//     for (int i = 0; i < numThreads; ++i) {
//         bslmt::ThreadUtil::join(consumerHandles[i]);
//     }
// }
// ```
//
///Example 2: Multi-Threaded Observer
/// - - - - - - - - - - - - - - - - -
// The previous example shows a simple mechanism for distributing work requests
// over multiple threads.  This approach works well for large tasks that can be
// decomposed into discrete, independent tasks that can benefit from parallel
// execution.  Note also that the various threads are synchronized only at the
// end of execution, when the Producer "joins" the various consumer threads.
//
// The simple strategy used in the first example works well for tasks that
// share no state, and are completely independent of one another.  For
// instance, a web server might use a similar strategy to distribute http
// requests across multiple worker threads.
//
// In more complicated examples, it is often necessary or desirable to
// synchronize the separate tasks during execution.  The second example below
// shows a single "Observer" mechanism that receives event notification from
// the various worker threads.
//
// We first create a simple `my_Event` data type.  Worker threads will use this
// data type to report information about their work.  In our example, we will
// report the "worker Id", the event number, and some arbitrary text.
//
// As with the previous example, class `my_Event` also contains an `EventType`,
// which is an enumeration which that indicates whether the worker has
// completed all work.  The "Observer" will use this enumerated value to note
// when a Worker thread has completed its work.
// ```
// enum {
//     k_MAX_CONSUMER_THREADS = 10,
//     k_MAX_EVENT_TEXT       = 80
// };
//
// struct my_Event {
//     enum EventType {
//         e_IN_PROGRESS   = 1,
//         e_TASK_COMPLETE = 2
//     };
//
//     EventType d_type;
//     int       d_workerId;
//     int       d_eventNumber;
//     char      d_eventText[k_MAX_EVENT_TEXT];
// };
// ```
// As noted in the previous example, `bslmt::ThreadUtil::create()` spawns a new
// thread, which invokes a simple "C" function taking a `void` pointer.  In the
// previous example, we simply converted that `void` pointer into a pointer to
// the parameterized `bdlcc::Queue<TYPE>` object.
//
// In this example, we want to pass an additional data item.  Each worker
// thread is initialized with a unique integer value ("worker Id") that
// identifies that thread.  We create a simple data structure that contains
// both of these values:
// ```
// struct my_WorkerData {
//     int                     d_workerId;
//     bdlcc::Queue<my_Event> *d_queue_p;
// };
// ```
// Function `myWorker` simulates a working thread by enqueuing multiple
// `my_Event` events during execution.  In a normal application, each
// `my_Event` structure would likely contain different textual information; for
// the sake of simplicity, our loop uses a constant value for the text field.
// ```
// void myWorker(int workerId, bdlcc::Queue<my_Event> *queue)
// {
//     const int NEVENTS = 5;
//     int evnum;
//
//     for (evnum = 0; evnum < NEVENTS; ++evnum) {
//         my_Event ev = {
//             my_Event::e_IN_PROGRESS,
//             workerId,
//             evnum,
//             "In-Progress Event"
//         };
//         queue->pushBack(ev);
//     }
//
//     my_Event ev = {
//         my_Event::e_TASK_COMPLETE,
//         workerId,
//         evnum,
//         "Task Complete"
//     };
//     queue->pushBack(ev);
// }
// ```
// The callback function invoked by `bslmt::ThreadUtil::create()` takes the
// traditional `void` pointer.  The expected data is the composite structure
// `my_WorkerData`.  The callback function casts the `void` pointer to the
// application-specific data type and then uses the referenced object to
// construct a call to the `myWorker` function.
// ```
// extern "C" void *myWorkerThread(void *v_worker_p)
// {
//     my_WorkerData *worker_p = (my_WorkerData *) v_worker_p;
//     myWorker(worker_p->d_workerId, worker_p->d_queue_p);
//     return v_worker_p;
// }
// ```
// For the sake of simplicity, we will implement the Observer behavior in the
// main thread.  The `void` function `myObserver` starts out multiple threads
// running the `myWorker` function, reads `my_Event`s from the queue, and logs
// all messages in the order of arrival.
//
// As each `myWorker` thread terminates, it sends a `e_TASK_COMPLETE` event.
// Upon receiving this event, the `myObserver` function uses the `d_workerId`
// to find the relevant thread, and then "joins" that thread.
//
// The `myObserver` function determines when all tasks have completed simply by
// counting the number of `e_TASK_COMPLETE` messages received.
// ```
// void myObserver()
// {
//     const int NTHREADS = 10;
//     bdlcc::Queue<my_Event> queue;
//
//     assert(NTHREADS > 0 && NTHREADS <= k_MAX_CONSUMER_THREADS);
//     bslmt::ThreadUtil::Handle workerHandles[k_MAX_CONSUMER_THREADS];
//
//     my_WorkerData workerData;
//     workerData.d_queue_p = &queue;
//     for (int i = 0; i < NTHREADS; ++i) {
//         workerData.d_workerId = i;
//         bslmt::ThreadUtil::create(&workerHandles[i],
//                                   myWorkerThread,
//                                   &workerData);
//     }
//     int nStop = 0;
//     while (nStop < NTHREADS) {
//         my_Event ev = queue.popFront();
//         bsl::cout << "[" << ev.d_workerId    << "] "
//                          << ev.d_eventNumber << ". "
//                          << ev.d_eventText   << bsl::endl;
//         if (my_Event::e_TASK_COMPLETE == ev.d_type) {
//             ++nStop;
//             bslmt::ThreadUtil::join(workerHandles[ev.d_workerId]);
//         }
//     }
// }
// ```

#include <bdlscm_version.h>

#include <bdlc_queue.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslmt_condition.h>
#include <bslmt_lockguard.h>
#include <bslmt_mutex.h>
#include <bslmt_threadutil.h>

#include <bslmf_movableref.h>

#include <bsls_libraryfeatures.h>
#include <bsls_timeinterval.h>

#include <bsl_vector.h>

#include <vector>

namespace BloombergLP {
namespace bdlcc {

                                // ===========
                                // class Queue
                                // ===========

/// This class provides a thread-enabled implementation of an efficient,
/// in-place, indexable, double-ended queue of parameterized `TYPE` values.
/// Very efficient access to the underlying `bdlc::Queue` object is provided,
/// as well as to a `bslmt::Mutex` and a `bslmt::Condition` variable, to
/// facilitate thread-safe use of the `bdlc::Queue`.  Note that `Queue` is not
/// a value-semantic type, but the underlying `bdlc::Queue` is.  In this
/// regard, `Queue` is a thread-enabled handle for a `bdlc::Queue`.
template <class TYPE>
class Queue {

    // PRIVATE TYPES
    typedef typename bdlc::Queue<TYPE>::InitialCapacity QueueCapacity;
                                           // We need this typedef to work
                                           // around a bug in Sun WorkShop 6
                                           // update 1: if the typedef is
                                           // replaced by its actual definition
                                           // in the two constructors
                                           // initialization list, the compiler
                                           // erroneously reports a syntax
                                           // error ("Expected an expression").

    template <class VECTOR>
    struct IsVector;

    // DATA
    mutable
    bslmt::Mutex      d_mutex;             // mutex object used to synchronize
                                           // access to this queue

    bslmt::Condition  d_notEmptyCondition; // condition variable used to signal
                                           // that new data is available in the
                                           // queue

    bslmt::Condition  d_notFullCondition;  // condition variable used to signal
                                           // when there is room available to
                                           // add new data to the queue

    bdlc::Queue<TYPE> d_queue;             // the queue, with allocator as last
                                           // data member

    const int         d_highWaterMark;     // positive maximum number of items
                                           // that can be queued before
                                           // insertions will be blocked, or
                                           // -1 if unlimited

  private:
    // NOT IMPLEMENTED
    Queue(const Queue<TYPE>&);
    Queue<TYPE>& operator=(const Queue<TYPE>&);

    // PRIVATE MANIPULATORS

    /// Remove all the items in this queue.  If the optionally specified
    /// `buffer` is not 0, load into `buffer` a copy of the items removed in
    /// front to back order of the queue prior to `removeAll`.
    template <class VECTOR>
    void removeAllImp(VECTOR *buffer = 0);

    /// Remove up to the specified `maxNumItems` from the front of this
    /// queue.  Optionally specify a `buffer` into which the items removed
    /// from the queue are loaded.  If `buffer` is non-null, the removed
    /// items are appended to it as if by repeated application of
    /// `buffer->push_back(popFront())` while the queue is not empty and
    /// `maxNumItems` have not yet been removed.  The behavior is undefined
    /// unless `maxNumItems >= 0`.  This method never blocks.
    template <class VECTOR>
    void tryPopFrontImp(int maxNumItems, VECTOR *buffer);

    /// Remove up to the specified `maxNumItems` from the back of this
    /// queue.  Optionally specify a `buffer` into which the items removed
    /// from the queue are loaded.  If `buffer` is non-null, the removed
    /// items are appended to it as if by repeated application of
    /// `buffer->push_back(popBack())` while the queue is not empty and
    /// `maxNumItems` have not yet been removed.  This method never blocks.
    /// The behavior is undefined unless `maxNumItems >= 0`.  Note that the
    /// ordering of the items in `*buffer` after the call is the reverse of
    /// the ordering they had in the queue.
    template <class VECTOR>
    void tryPopBackImp(int maxNumItems, VECTOR *buffer);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Queue, bslma::UsesBslmaAllocator);

    // TYPES

    /// Enable uniform use of an optional integral constructor argument to
    /// specify the initial internal capacity (in items).  For example,
    /// ```
    ///  const Queue<int>::InitialCapacity NUM_ITEMS(8));
    ///  Queue<int> x(NUM_ITEMS);
    /// ```
    /// defines an instance `x` with an initial capacity of 8 items, but
    /// with a logical length of 0 items.
    struct InitialCapacity {

        // DATA
        unsigned int d_i;

        // CREATORS

        /// Create an object with the specified value `i`.
        explicit InitialCapacity(int i)
        : d_i(i)
        {}
    };

    // CREATORS

    /// Create a queue of objects of parameterized `TYPE`.  Optionally
    /// specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.
    explicit
    Queue(bslma::Allocator *basicAllocator = 0);

    /// Create a queue of objects of parameterized `TYPE` having either the
    /// specified `highWaterMark` suggested maximum length if
    /// `highWaterMark` is positive, or no maximum length if `highWaterMark`
    /// is negative.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  The behavior is undefined unless
    /// `highWaterMark != 0`.
    explicit
    Queue(int highWaterMark, bslma::Allocator *basicAllocator = 0);

    /// Create a queue of objects of parameterized `TYPE` with sufficient
    /// initial capacity to accommodate up to the specified `numItems`
    /// values without subsequent reallocation.  Optionally specify a
    /// `basicAllocator` used to supply memory.  If `basicAllocator` is 0,
    /// the currently installed default allocator is used.
    explicit
    Queue(const InitialCapacity&  numItems,
          bslma::Allocator       *basicAllocator = 0);

    /// Create a queue of objects of parameterized `TYPE` with sufficient
    /// initial capacity to accommodate up to the specified `numItems`
    /// values without subsequent reallocation and having either the
    /// specified `highWaterMark` suggested maximum length if
    /// `highWaterMark` is positive, or no maximum length if `highWaterMark`
    /// is negative.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  The behavior is undefined unless
    /// `highWaterMark != 0`.
    Queue(const InitialCapacity&  numItems,
          int                     highWaterMark,
          bslma::Allocator       *basicAllocator = 0);

    /// Create a queue of objects of parameterized `TYPE` containing the
    /// sequence of `TYPE` values from the specified `srcQueue`.  Optionally
    /// specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.
    Queue(const bdlc::Queue<TYPE>&  srcQueue,
          bslma::Allocator         *basicAllocator = 0);            // IMPLICIT

    /// Create a queue of objects of parameterized `TYPE` containing the
    /// sequence of `TYPE` values from the specified `srcQueue` and having
    /// either the specified `highWaterMark` suggested maximum length if
    /// `highWaterMark` is positive, or no maximum length if `highWaterMark`
    /// is negative.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  The behavior is undefined unless
    /// `highWaterMark != 0`.
    Queue(const bdlc::Queue<TYPE>&  srcQueue,
          int                       highWaterMark,
          bslma::Allocator         *basicAllocator = 0);

    /// Destroy this container.  The behavior is undefined unless all access
    /// or modification of the container has completed prior to this call.
    ~Queue();

    // MANIPULATORS

    /// Remove the last item in this queue and load that item into the
    /// specified `buffer`.  If this queue is empty, block until an item is
    /// available.
    void popBack(TYPE *buffer);

    /// Remove the last item in this queue and return that item value.  If
    /// this queue is empty, block until an item is available.
    TYPE popBack();

    /// Remove the last item in this queue and load that item value into the
    /// specified `buffer`.  If this queue is empty, block until an item is
    /// available or until the specified `timeout` (expressed as the
    /// **ABSOLUTE** time from 00:00:00 UTC, January 1, 1970) expires.  Return
    /// 0 on success, and a non-zero value if the call timed out before an
    /// item was available.
    int timedPopBack(TYPE *buffer, const bsls::TimeInterval& timeout);

    /// Remove the first item in this queue and load that item into the
    /// specified `buffer`.  If the queue is empty, block until an item is
    /// available.
    void popFront(TYPE *buffer);

    /// Remove the first item in this queue and return that item value.  If
    /// the queue is empty, block until an item is available.
    TYPE popFront();

    /// Remove the first item in this queue and load that item value into
    /// the specified `buffer`.  If this queue is empty, block until an item
    /// is available or until the specified `timeout` (expressed as the
    /// **ABSOLUTE** time from 00:00:00 UTC, January 1, 1970) expires.  Return
    /// 0 on success, and a non-zero value if the call timed out before an
    /// item was available.
    int timedPopFront(TYPE *buffer, const bsls::TimeInterval& timeout);

    /// Remove all the items in this queue.  If the optionally specified
    /// `buffer` is not 0, load into `buffer` a copy of the items removed in
    /// front to back order of the queue prior to `removeAll`.
    void removeAll();
    void removeAll(bsl::vector<TYPE>      *buffer);
    void removeAll(std::vector<TYPE>      *buffer);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    void removeAll(std::pmr::vector<TYPE> *buffer);
#endif

    /// Append the specified `item` to the back of this queue.  If the
    /// high-water mark is non-negative and the number of items in this
    /// queue is greater than or equal to the high-water mark, then block
    /// until the number of items in this queue is less than the high-water
    /// mark.
    void pushBack(const TYPE& item);

    /// Append the specified `item` to the front of this queue.  If the
    /// high-water mark is non-negative and the number of items in this
    /// queue is greater than or equal to the high-water mark, then block
    /// until the number of items in this queue is less than the high-water
    /// mark.
    void pushFront(const TYPE& item);

    /// Append the specified `item` to the back of this queue.  If the
    /// high-water mark is non-negative and the number of items in this
    /// queue is greater than or equal to the high-water mark, then block
    /// until the number of items in this queue is less than the high-water
    /// mark or until the specified `timeout` (expressed as the **ABSOLUTE**
    /// time from 00:00:00 UTC, January 1, 1970) expires.  Return 0 on
    /// success, and a non-zero value if the call timed out before the
    /// number of items in this queue fell below the high-water mark.
    int timedPushBack(const TYPE& item, const bsls::TimeInterval& timeout);

    /// Append the specified `item` to the front of this queue.  If the high
    /// water mark is non-negative and the number of items in this queue is
    /// greater than or equal to the high-water mark, then block until the
    /// number of items in this queue is less than the high-water mark or
    /// until the specified `timeout` (expressed as the **ABSOLUTE** time from
    /// 00:00:00 UTC, January 1, 1970) expires.  Return 0 on success, and a
    /// non-zero value if the call timed out before the number of items in
    /// this queue fell below the high-water mark.
    int timedPushFront(const TYPE& item, const bsls::TimeInterval& timeout);

    /// Append the specified `item` to the front of this queue without
    /// regard for the high-water mark.  Note that this method is provided
    /// to allow high priority items to be inserted when the queue is full
    /// (i.e., has a number of items greater than or equal to its high-water
    /// mark); `pushFront` and `pushBack` should be used for general use.
    void forcePushFront(const TYPE& item);

    /// If this queue is non-empty, remove the first item, load that item
    /// into the specified `buffer`, and return 0 indicating success.  If
    /// this queue is empty, return a non-zero value with no effect on
    /// `buffer` or the state of this queue.  This method never blocks.
    int tryPopFront(TYPE *buffer);

    // Remove up to the specified `maxNumItems` from the front of this
    // queue.  Optionally specify a `buffer` into which the items removed
    // from the queue are loaded.  If `buffer` is non-null, the removed
    // items are appended to it as if by repeated application of
    // `buffer->push_back(popFront())` while the queue is not empty and
    // `maxNumItems` have not yet been removed.  The behavior is undefined
    // unless `maxNumItems >= 0`.  This method never blocks.
    void tryPopFront(int maxNumItems);
    void tryPopFront(int maxNumItems, bsl::vector<TYPE>      *buffer);
    void tryPopFront(int maxNumItems, std::vector<TYPE>      *buffer);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    void tryPopFront(int maxNumItems, std::pmr::vector<TYPE> *buffer);
#endif

    /// If this queue is non-empty, remove the last item, load that item into
    /// the specified `buffer`, and return 0 indicating success.  If this queue
    /// is empty, return a non-zero value with no effect on `buffer` or the
    /// state of this queue.  This method never blocks.
    int tryPopBack(TYPE *buffer);

    // Remove up to the specified `maxNumItems` from the back of this
    // queue.  Optionally specify a `buffer` into which the items removed
    // from the queue are loaded.  If `buffer` is non-null, the removed
    // items are appended to it as if by repeated application of
    // `buffer->push_back(popBack())` while the queue is not empty and
    // `maxNumItems` have not yet been removed.  This method never blocks.
    // The behavior is undefined unless `maxNumItems >= 0`.  Note that the
    // ordering of the items in `*buffer` after the call is the reverse of
    // the ordering they had in the queue.
    void tryPopBack(int maxNumItems);
    void tryPopBack(int maxNumItems, bsl::vector<TYPE>      *buffer);
    void tryPopBack(int maxNumItems, std::vector<TYPE>      *buffer);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    void tryPopBack(int maxNumItems, std::pmr::vector<TYPE> *buffer);
#endif

    // *** Modifiable access to the mutex, condition variable, and queue ***

    /// Return a reference to the modifiable condition variable used by this
    /// queue to signal that the queue is not empty.
    ///
    /// @DEPRECATED: Use `notEmptyCondition` instead.
    bslmt::Condition& condition();

    /// Return a reference to the modifiable condition variable used by this
    /// queue to signal that the queue is not full (i.e., has fewer items
    /// than its high-water mark).
    ///
    /// @DEPRECATED: Use `notFullCondition` instead.
    bslmt::Condition& insertCondition();

    /// Return a reference to the modifiable mutex used by this queue to
    /// synchronize access to its underlying `bdlc::Queue` object.
    bslmt::Mutex& mutex();

    /// Return the condition variable used by this queue to signal that the
    /// queue is not empty.
    bslmt::Condition& notEmptyCondition();

    /// Return the condition variable used by this queue to signal that the
    /// queue is not full (i.e., has fewer items than its high-water mark).
    bslmt::Condition& notFullCondition();

    /// Return a reference to the modifiable underlying `bdlc::Queue` object
    /// used by this queue.  Any access to the returned queue MUST first
    /// lock the associated mutex object (see the `mutex` method) in a
    /// multi-threaded environment.  And when items are directly added to
    /// the queue returned by this method, the associated condition variable
    /// (see the `condition` method) should be signaled to notify any
    /// waiting threads of the availability of the new data.
    ///
    /// The (error-prone) usage of this method will be replaced by an
    /// appropriate smart-pointer-like proctor object in the future.
    /// Meanwhile, use this method with caution.
    bdlc::Queue<TYPE>& queue();

    // ACCESSORS

    /// Return the high-water mark value for this queue.  Note that a negative
    /// value indicates no suggested-maximum capacity, and is not necessarily
    /// the same negative value that was passed to the constructor.
    int highWaterMark() const;

    /// Return the number of elements in this queue.  Note that if other
    /// threads are manipulating the queue, this information may be obsolete by
    /// the time it is returned.
    int length() const;
};

                        // ======================
                        // struct Queue::IsVector
                        // ======================

/// This `struct` has a `value` that evaluates to `true` if the specified
/// `VECTOR` is a `bsl`, `std`, or `std::pmr` `vector<TYPE>`.
template <class TYPE>
template <class VECTOR>
struct Queue<TYPE>::IsVector {

    static const bool value =
                            bsl::is_same<bsl::vector<TYPE>, VECTOR>::value
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
                         || bsl::is_same<std::pmr::vector<TYPE>, VECTOR>::value
#endif
                         || bsl::is_same<std::vector<TYPE>, VECTOR>::value;
};

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

// PRIVATE MANIPULATORS
template <class TYPE>
template <class VECTOR>
void Queue<TYPE>::removeAllImp(VECTOR *buffer)
{
    BSLMF_ASSERT(IsVector<VECTOR>::value);

    bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
    bool wasFull = d_highWaterMark > 0 && d_queue.length() >= d_highWaterMark;

    if (buffer) {
        for (int ii = 0, len = d_queue.length(); ii < len; ++ii) {
            buffer->push_back(bslmf::MovableRefUtil::move(d_queue[ii]));
        }
    }
    d_queue.removeAll();

    lock.release()->unlock();

    if (wasFull) {
        for (int i = 0; d_highWaterMark > i; ++i) {
            d_notFullCondition.signal();
        }
    }
}

template <class TYPE>
template <class VECTOR>
void Queue<TYPE>::tryPopFrontImp(int maxNumItems, VECTOR *buffer)
{
    BSLMF_ASSERT(IsVector<VECTOR>::value);

    int numSignal = 0;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        int        length  = d_queue.length();
        const bool wasFull = d_highWaterMark > 0 && length >= d_highWaterMark;

        for (; d_queue.length() > 0 && maxNumItems > 0; --maxNumItems) {
            if (buffer) {
                buffer->push_back(
                                 bslmf::MovableRefUtil::move(d_queue.front()));
            }
            d_queue.popFront();
            --length;
        }

        if (wasFull && length < d_highWaterMark) {
            numSignal = d_highWaterMark - length;
        }
    }

    for (; 0 < numSignal; --numSignal) {
        d_notFullCondition.signal();
    }
}

template <class TYPE>
template <class VECTOR>
void Queue<TYPE>::tryPopBackImp(int maxNumItems, VECTOR *buffer)
{
    BSLMF_ASSERT(IsVector<VECTOR>::value);

    int numSignal = 0;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        int        length  = d_queue.length();
        const bool wasFull = d_highWaterMark > 0 && length >= d_highWaterMark;

        for (; d_queue.length() > 0 && maxNumItems > 0; --maxNumItems) {
            if (buffer) {
                buffer->push_back(bslmf::MovableRefUtil::move(d_queue.back()));
            }
            d_queue.popBack();
            --length;
        }

        if (wasFull && length < d_highWaterMark) {
            numSignal = d_highWaterMark - length;
        }
    }

    for (; 0 < numSignal; --numSignal) {
        d_notFullCondition.signal();
    }
}

// CREATORS
template <class TYPE>
inline
Queue<TYPE>::Queue(bslma::Allocator *basicAllocator)
: d_queue(basicAllocator)
, d_highWaterMark(-1)
{
}

template <class TYPE>
inline
Queue<TYPE>:: Queue(const InitialCapacity&  numItems,
                    bslma::Allocator       *basicAllocator)
: d_queue(QueueCapacity(numItems.d_i), basicAllocator)
, d_highWaterMark(-1)
{
}

template <class TYPE>
inline
Queue<TYPE>::Queue(int highWaterMark, bslma::Allocator *basicAllocator)
: d_queue(basicAllocator)
, d_highWaterMark(highWaterMark < 0 ? -1 : highWaterMark)
{
}

template <class TYPE>
inline
Queue<TYPE>::Queue(const InitialCapacity&  numItems,
                   int                     highWaterMark,
                   bslma::Allocator       *basicAllocator)
: d_queue(QueueCapacity(numItems.d_i), basicAllocator)
, d_highWaterMark(highWaterMark < 0 ? -1 : highWaterMark)
{
}

template <class TYPE>
inline
Queue<TYPE>::Queue(const bdlc::Queue<TYPE>&  srcQueue,
                   bslma::Allocator         *basicAllocator)
: d_queue(srcQueue, basicAllocator)
, d_highWaterMark(-1)
{
}

template <class TYPE>
inline
Queue<TYPE>::Queue(const bdlc::Queue<TYPE>&  srcQueue,
                   int                       highWaterMark,
                   bslma::Allocator         *basicAllocator)
: d_queue(srcQueue, basicAllocator)
, d_highWaterMark(highWaterMark < 0 ? -1 : highWaterMark)
{
}

template <class TYPE>
inline
Queue<TYPE>::~Queue()
{
}

// MANIPULATORS
template <class TYPE>
void Queue<TYPE>::popBack(TYPE *buffer)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        while (0 == (length = d_queue.length())) {
            d_notEmptyCondition.wait(&d_mutex);
        }
        *buffer = d_queue.back();
        d_queue.popBack();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
}

template <class TYPE>
TYPE Queue<TYPE>::popBack()
{
    // Note that this method is not implemented in terms of 'popBack(TYPE*)'
    // because that would require TYPE to have a default constructor.

    unsigned int length;

    bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

    while (0 == (length = d_queue.length())) {
        d_notEmptyCondition.wait(&d_mutex);
    }
    TYPE back = d_queue.back();
    d_queue.popBack();
    --length;

    lock.release()->unlock();

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return back;
}

template <class TYPE>
int Queue<TYPE>::timedPopBack(TYPE *buffer, const bsls::TimeInterval& timeout)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        while (0 == (length = d_queue.length())) {
            if (d_notEmptyCondition.timedWait(&d_mutex, timeout)) {
                return 1;                                             // RETURN
            }
        }
        *buffer = d_queue.back();
        d_queue.popBack();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return 0;
}

template <class TYPE>
void Queue<TYPE>::popFront(TYPE *buffer)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        while (0 == (length = d_queue.length())) {
            d_notEmptyCondition.wait(&d_mutex);
        }
        *buffer = d_queue.front();
        d_queue.popFront();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
}

template <class TYPE>
TYPE Queue<TYPE>::popFront()
{
    // Note that this method is not implemented in terms of 'popFront(TYPE*)'
    // because that would require TYPE to have a default constructor.

    unsigned int length;

    bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

    while (0 == (length = d_queue.length())) {
        d_notEmptyCondition.wait(&d_mutex);
    }
    TYPE front = d_queue.front();
    d_queue.popFront();
    --length;

    lock.release()->unlock();

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return front;
}

template <class TYPE>
int Queue<TYPE>::timedPopFront(TYPE *buffer, const bsls::TimeInterval& timeout)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        while (0 == (length = d_queue.length())) {
            if (d_notEmptyCondition.timedWait(&d_mutex, timeout)) {
                return 1;                                             // RETURN
            }
        }
        *buffer = d_queue.front();
        d_queue.popFront();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return 0;
}

template <class TYPE>
int Queue<TYPE>::tryPopFront(TYPE *buffer)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        if (0 == (length = d_queue.length())) {
            return 1;                                                 // RETURN
        }
        *buffer = d_queue.front();
        d_queue.popFront();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return 0;
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopFront(int maxNumItems)
{
    tryPopFrontImp(maxNumItems, static_cast<bsl::vector<TYPE> *>(0));
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopFront(int maxNumItems, bsl::vector<TYPE> *buffer)
{
    tryPopFrontImp(maxNumItems, buffer);
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopFront(int maxNumItems, std::vector<TYPE> *buffer)
{
    tryPopFrontImp(maxNumItems, buffer);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
template <class TYPE>
inline
void Queue<TYPE>::tryPopFront(int maxNumItems, std::pmr::vector<TYPE> *buffer)
{
    tryPopFrontImp(maxNumItems, buffer);
}
#endif

template <class TYPE>
int Queue<TYPE>::tryPopBack(TYPE *buffer)
{
    unsigned int length;
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

        if (0 == (length = d_queue.length())) {
            return 1;                                                 // RETURN
        }
        *buffer = d_queue.back();
        d_queue.popBack();
        --length;
    }

    if (length < (unsigned) d_highWaterMark) {
        d_notFullCondition.signal();
    }
    return 0;
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopBack(int maxNumItems)
{
    tryPopBackImp(maxNumItems, static_cast<bsl::vector<TYPE> *>(0));
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopBack(int maxNumItems, bsl::vector<TYPE> *buffer)
{
    tryPopBackImp(maxNumItems, buffer);
}

template <class TYPE>
inline
void Queue<TYPE>::tryPopBack(int maxNumItems, std::vector<TYPE> *buffer)
{
    tryPopBackImp(maxNumItems, buffer);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
template <class TYPE>
inline
void Queue<TYPE>::tryPopBack(int maxNumItems, std::pmr::vector<TYPE> *buffer)
{
    tryPopBackImp(maxNumItems, buffer);
}
#endif

template <class TYPE>
void Queue<TYPE>::removeAll()
{
    removeAllImp(static_cast<bsl::vector<TYPE> *>(0));
}

template <class TYPE>
void Queue<TYPE>::removeAll(bsl::vector<TYPE> *buffer)
{
    removeAllImp(buffer);
}

template <class TYPE>
void Queue<TYPE>::removeAll(std::vector<TYPE> *buffer)
{
    removeAllImp(buffer);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
template <class TYPE>
void Queue<TYPE>::removeAll(std::pmr::vector<TYPE> *buffer)
{
    removeAllImp(buffer);
}
#endif

template <class TYPE>
void Queue<TYPE>::pushBack(const TYPE& item)
{
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
        if (d_highWaterMark >= 0) {
            while (d_queue.length() >= d_highWaterMark) {
                d_notFullCondition.wait(&d_mutex);
            }
        }
        d_queue.pushBack(item);
    }

    d_notEmptyCondition.signal();
}

template <class TYPE>
void Queue<TYPE>::pushFront(const TYPE& item)
{
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
        if (d_highWaterMark >= 0) {
            while (d_queue.length() >= d_highWaterMark) {
                d_notFullCondition.wait(&d_mutex);
            }
        }
        d_queue.pushFront(item);
    }

    d_notEmptyCondition.signal();
}

template <class TYPE>
int Queue<TYPE>::timedPushBack(const TYPE&               item,
                               const bsls::TimeInterval& timeout)
{
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
        if (d_highWaterMark >= 0) {
            while (d_queue.length() >= d_highWaterMark) {
                if (d_notFullCondition.timedWait(&d_mutex, timeout)) {
                    return 1;                                         // RETURN
                }
            }
        }
        d_queue.pushBack(item);
    }

    d_notEmptyCondition.signal();
    return 0;
}

template <class TYPE>
int Queue<TYPE>::timedPushFront(const TYPE&               item,
                                const bsls::TimeInterval& timeout)
{
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
        if (d_highWaterMark >= 0) {
            while (d_queue.length() >= d_highWaterMark) {
                if (d_notFullCondition.timedWait(&d_mutex, timeout)) {
                    return 1;                                         // RETURN
                }
            }
        }
        d_queue.pushFront(item);
    }

    d_notEmptyCondition.signal();
    return 0;
}

template <class TYPE>
inline
void Queue<TYPE>::forcePushFront(const TYPE& item)
{
    {
        bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);
        d_queue.pushFront(item);
    }
    d_notEmptyCondition.signal();
}

// *** Modifiable access to the mutex, condition variable, and queue ***

template <class TYPE>
inline
bslmt::Condition& Queue<TYPE>::condition()
{
    return d_notEmptyCondition;
}

template <class TYPE>
inline
bslmt::Condition& Queue<TYPE>::insertCondition()
{
    return d_notFullCondition;
}

template <class TYPE>
inline
bslmt::Mutex& Queue<TYPE>::mutex()
{
    return d_mutex;
}

template <class TYPE>
inline
bslmt::Condition& Queue<TYPE>::notEmptyCondition()
{
    return d_notEmptyCondition;
}

template <class TYPE>
inline
bslmt::Condition& Queue<TYPE>::notFullCondition()
{
    return d_notFullCondition;
}

template <class TYPE>
inline
bdlc::Queue<TYPE>& Queue<TYPE>::queue()
{
    return d_queue;
}

// ACCESSORS
template <class TYPE>
inline
int Queue<TYPE>::highWaterMark() const
{
    return d_highWaterMark;
}

template <class TYPE>
inline
int Queue<TYPE>::length() const
{
    bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex);

    return d_queue.length();
}

}  // 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 ----------------------------------
