// bdlcc_singleproducerqueue.h                                        -*-C++-*-

#ifndef INCLUDED_BDLCC_SINGLEPRODUCERQUEUE
#define INCLUDED_BDLCC_SINGLEPRODUCERQUEUE

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

//@PURPOSE: Provide a thread-aware single producer queue of values.
//
//@CLASSES:
//  bdlcc::SingleProducerQueue: thread-aware single producer queue of 'TYPE'
//
//@DESCRIPTION: This component defines a type, 'bdlcc::SingleProducerQueue',
// that provides an efficient, thread-aware queue of values assuming a single
// producer (the use of 'pushBack' and 'tryPushBack' is done by one thread or a
// group of threads using external synchronization).  The behavior of the
// methods 'pushBack' and 'tryPushBack' is undefined unless the use is by a
// single producer.  This class is ideal for synchronization and communication
// between threads in a producer-consumer model when there is only one producer
// thread.
//
// The queue provides 'pushBack' and 'popFront' methods for pushing data into
// the queue and popping data from the queue.  The queue will allocate memory
// as necessary to accommodate 'pushBack' invocations ('pushBack' will never
// block and is provided for consistency with other containers).  When the
// queue is empty, the 'popFront' methods block until data appears in the
// queue.  Non-blocking methods 'tryPushBack' and 'tryPopFront' are also
// provided.  The 'tryPopFront' method fails immediately, returning a non-zero
// value, if the queue is empty.
//
// The queue may be placed into a "enqueue disabled" state using the
// 'disablePushBack' method.  When disabled, 'pushBack' and 'tryPushBack' fail
// immediately and return an error code.  The queue may be restored to normal
// operation with the 'enablePushBack' method.
//
// The queue may be placed into a "dequeue disabled" state using the
// 'disablePopFront' method.  When dequeue disabled, 'popFront' and
// 'tryPopFront' fail immediately and return an error code.  Any threads
// blocked in 'popFront' when the queue is dequeue disabled return from
// 'popFront' immediately and return an error code.
//
///Template Requirements
///---------------------
// 'bdlcc::SingleProducerQueue' 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 constructor, 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::SingleProducerQueue' is exception neutral, and all of the methods
// of 'bdlcc::SingleProducerQueue' provide the basic exception safety guarantee
// (see 'bsldoc_glossary').
//
///Move Semantics in C++03
///-----------------------
// Move-only types are supported by 'bdlcc::SingleProducerQueue' 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::SingleProducerQueue' 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(&item)' 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(&item)', and 'bdlcc::SingleProducerQueue' guarantees that
// each thread gets a unique element from the queue:
//..
//  void myConsumer(bdlcc::SingleProducerQueue<my_WorkRequest> *queue)
//  {
//      while (1) {
//          // 'popFront()' will wait for a 'my_WorkRequest' until available.
//
//          my_WorkRequest item;
//          queue->popFront(&item);
//          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::SingleProducerQueue', 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_NUM_WORK_ITEMS = 1000
//      };
//
//      bdlcc::SingleProducerQueue<my_WorkRequest> queue;
//
//      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_singleproducerqueueimpl.h>

#include <bslalg_scalarprimitives.h>

#include <bslma_usesbslmaallocator.h>

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

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

#include <bsls_atomicoperations.h>

namespace BloombergLP {
namespace bdlcc {

                        // =========================
                        // class SingleProducerQueue
                        // =========================

template <class TYPE>
class SingleProducerQueue {
    // This class provides a thread-safe unbounded queue of values that assumes
    // a single producer thread.

    // PRIVATE TYPES
    typedef SingleProducerQueueImpl<TYPE,
                                    bsls::AtomicOperations,
                                    bslmt::Mutex,
                                    bslmt::Condition> Impl;

    // DATA
    Impl d_impl;

    // NOT IMPLEMENTED
    SingleProducerQueue(const SingleProducerQueue&);
    SingleProducerQueue& operator=(const SingleProducerQueue&);

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

    // PUBLIC TYPES
    typedef TYPE value_type;  // The type for elements.

    // PUBLIC CONSTANTS
    enum {
        e_SUCCESS  = Impl::e_SUCCESS,  // must be 0
        e_EMPTY    = Impl::e_EMPTY,
        e_DISABLED = Impl::e_DISABLED
    };

    // CREATORS
    SingleProducerQueue(bslma::Allocator *basicAllocator = 0);
        // Create a thread-aware queue.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    SingleProducerQueue(bsl::size_t       capacity,
                        bslma::Allocator *basicAllocator = 0);
        // Create a thread-aware queue with, at least, the specified
        // 'capacity'.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    //! ~SingleProducerQueue() = default;
        // Destroy this object.

    // MANIPULATORS
    int 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.  Return 0 on success, and a non-zero value
        // otherwise.  Specifically, return 'e_DISABLED' if
        // 'isPopFrontDisabled()'.  On failure, 'value' is not changed.
        // Threads blocked due to the queue being empty will return
        // 'e_DISABLED' if 'disablePopFront' is invoked.

    int pushBack(const TYPE& value);
        // Append the specified 'value' to the back of this queue.  Return 0 on
        // success, and a non-zero value otherwise.  Specifically, return
        // 'e_DISABLED' if 'isPushBackDisabled()'.  The behavior is undefined
        // unless the invoker of this method is the single producer.

    int pushBack(bslmf::MovableRef<TYPE> value);
        // Append the specified move-insertable 'value' to the back of this
        // queue.  'value' is left in a valid but unspecified state.  Return 0
        // on success, and a non-zero value otherwise.  Specifically, return
        // 'e_DISABLED' if 'isPushBackDisabled()'.  On failure, 'value' is not
        // changed.  The behavior is undefined unless the invoker of this
        // method is the single producer.

    void removeAll();
        // Remove all items currently in 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.

    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
        // otherwise.  Specifically, return 'e_DISABLED' if
        // 'isPopFrontDisabled()', and 'e_EMPTY' if '!isPopFrontDisabled()' and
        // the queue was empty.  On failure, 'value' is not changed.

    int tryPushBack(const TYPE& value);
        // Append the specified 'value' to the back of this queue.  Return 0 on
        // success, and a non-zero value otherwise.  Specifically, return
        // 'e_DISABLED' if 'isPushBackDisabled()'.  The behavior is undefined
        // unless the invoker of this method is the single producer.

    int tryPushBack(bslmf::MovableRef<TYPE> value);
        // Append the specified move-insertable 'value' to the back of this
        // queue.  'value' is left in a valid but unspecified state.  Return 0
        // on success, and a non-zero value otherwise.  Specifically, return
        // 'e_DISABLED' if 'isPushBackDisabled()'.  On failure, 'value' is not
        // changed.  The behavior is undefined unless the invoker of this
        // method is the single producer.

                       // Enqueue/Dequeue State

    void disablePopFront();
        // Disable dequeueing from this queue.  All subsequent invocations of
        // 'popFront' or 'tryPopFront' will fail immediately.  All blocked
        // invocations of 'popFront' and 'waitUntilEmpty' will fail
        // immediately.  If the queue is already dequeue disabled, this method
        // has no effect.

    void disablePushBack();
        // Disable enqueueing into 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 enqueue disabled, this method has no effect.

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

    void enablePopFront();
        // Enable dequeueing.  If the queue is not dequeue disabled, this call
        // has no effect.

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

    bool isFull() const;
        // Return 'true' if this queue is full (has no available capacity), or
        // 'false' otherwise.  Note that for unbounded queues, this method
        // always returns 'false'.

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

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

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

    int waitUntilEmpty() const;
        // Block until all the elements in this queue are removed.  Return 0 on
        // success, and a non-zero value otherwise.  Specifically, return
        // 'e_DISABLED' if '!isEmpty() && isPopFrontDisabled()'.  A blocked
        // thread waiting for the queue to empty will return 'e_DISABLED' if
        // 'disablePopFront' is invoked.  The behavior is undefined unless the
        // invoker of this method is the single producer.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.
};

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

                        // -------------------------
                        // class SingleProducerQueue
                        // -------------------------

// CREATORS
template <class TYPE>
SingleProducerQueue<TYPE>::SingleProducerQueue(
                                              bslma::Allocator *basicAllocator)
: d_impl(basicAllocator)
{
}

template <class TYPE>
SingleProducerQueue<TYPE>::SingleProducerQueue(
                                              bsl::size_t       capacity,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, basicAllocator)
{
}

// MANIPULATORS
template <class TYPE>
int SingleProducerQueue<TYPE>::popFront(TYPE *value)
{
    return d_impl.popFront(value);
}

template <class TYPE>
int SingleProducerQueue<TYPE>::pushBack(const TYPE& value)
{
    return d_impl.pushBack(value);
}

template <class TYPE>
int SingleProducerQueue<TYPE>::pushBack(bslmf::MovableRef<TYPE> value)
{
    return d_impl.pushBack(bslmf::MovableRefUtil::move(value));
}

template <class TYPE>
void SingleProducerQueue<TYPE>::removeAll()
{
    d_impl.removeAll();
}

template <class TYPE>
int SingleProducerQueue<TYPE>::tryPopFront(TYPE *value)
{
    return d_impl.tryPopFront(value);
}

template <class TYPE>
int SingleProducerQueue<TYPE>::tryPushBack(const TYPE& value)
{
    return d_impl.tryPushBack(value);
}

template <class TYPE>
int SingleProducerQueue<TYPE>::tryPushBack(bslmf::MovableRef<TYPE> value)
{
    return d_impl.tryPushBack(bslmf::MovableRefUtil::move(value));
}

                       // Enqueue/Dequeue State

template <class TYPE>
void SingleProducerQueue<TYPE>::disablePopFront()
{
    d_impl.disablePopFront();
}

template <class TYPE>
void SingleProducerQueue<TYPE>::disablePushBack()
{
    d_impl.disablePushBack();
}

template <class TYPE>
void SingleProducerQueue<TYPE>::enablePopFront()
{
    d_impl.enablePopFront();
}

template <class TYPE>
void SingleProducerQueue<TYPE>::enablePushBack()
{
    d_impl.enablePushBack();
}

// ACCESSORS
template <class TYPE>
bool SingleProducerQueue<TYPE>::isEmpty() const
{
    return d_impl.isEmpty();
}

template <class TYPE>
bool SingleProducerQueue<TYPE>::isFull() const
{
    return d_impl.isFull();
}

template <class TYPE>
bool SingleProducerQueue<TYPE>::isPopFrontDisabled() const
{
    return d_impl.isPopFrontDisabled();
}

template <class TYPE>
bool SingleProducerQueue<TYPE>::isPushBackDisabled() const
{
    return d_impl.isPushBackDisabled();
}

template <class TYPE>
bsl::size_t SingleProducerQueue<TYPE>::numElements() const
{
    return d_impl.numElements();
}

template <class TYPE>
int SingleProducerQueue<TYPE>::waitUntilEmpty() const
{
    return d_impl.waitUntilEmpty();
}

                                  // Aspects

template <class TYPE>
bslma::Allocator *SingleProducerQueue<TYPE>::allocator() const
{
    return d_impl.allocator();
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 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 ----------------------------------