// bdlma_concurrentpoolallocator.h                                    -*-C++-*-
#ifndef INCLUDED_BDLMA_CONCURRENTPOOLALLOCATOR
#define INCLUDED_BDLMA_CONCURRENTPOOLALLOCATOR

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

//@PURPOSE: Provide thread-safe memory-pooling allocator of fixed-size blocks.
//
//@CLASSES:
//   bdlma::ConcurrentPoolAllocator: thread-safe allocator of pooled blocks
//
//@DESCRIPTION: This component defines a class,
// `bdlma::ConcurrentPoolAllocator`, that implements the `bslma::Allocator`
// protocol and provides a thread-safe allocator of pooled memory blocks of
// uniform size (the "pooled size").  The pooled size is either (1) configured
// at construction, or (2) equal to the size of the first block allocated
// through the allocator.  All of the allocation requests of sizes up to the
// pooled size are satisfied with blocks from the underlying pool.  All
// requests of sizes larger than the pooled size will be satisfied through the
// external allocator (the allocator supplied at construction, or the default
// allocator if no allocator was provided).
//
///Protocol Hierarchy
///------------------
// The interface hierarchy (defined by direct public inheritance) of
// `bdlma::ConcurrentPoolAllocator` is as follows:
// ```
//  ,-------------------.
// ( bdlma::ConcurrentPoolAllocator )
//  `-------------------'
//            |       ctor/dtor
//            |       objectSize
//            V
//    ,----------------.
//   ( bslma::Allocator )
//    `----------------'
//                    allocate
//                    deallocate
// ```
// `bdlma::ConcurrentPoolAllocator` provides a concrete, thread-safe
// implementation of the `bslma::Allocator` protocol.
//
///Usage
///-----
// The `bdlma::ConcurrentPoolAllocator` is intended to be used in either of the
// following two cases.
//
// The first case is where frequent allocation and deallocation of memory
// occurs through the `bslma::Allocator` protocol and all of the allocated
// blocks have the same size.  In this case, the size of blocks to pool is
// determined the first time `allocate` is called and need not be specified at
// construction.
//
// The second case is where frequent allocation and deallocation of memory
// occurs through the `bslma::Allocator` protocol, most of the allocations have
// similar sizes, and a likely maximum for the largest allocation is known at
// the time of construction.
//
///Example 1: Uniform Sized Allocations
/// - - - - - - - - - - - - - - - - - -
// The following example illustrates the use of
// `bdlma::ConcurrentPoolAllocator` when all allocations are of uniform size.
// A `bdlma::ConcurrentPoolAllocator` is used in the implementation of a "work
// queue" where each "item" enqueued by a producer thread is of identical size.
// Concurrently, a consumer dequeues each work item when it becomes available,
// verifies the content (a sequence number in ASCII), and deallocates the work
// item.  The concurrent allocations and deallocations are valid because
// `bdlma::ConcurrentPoolAllocator` is thread-safe.
//
// First, an abstract of the example will be given with focus and commentary on
// the relevant details of `bdlma::ConcurrentPoolAllocator`.  Details
// pertaining to queue management, thread creation, thread synchronization,
// etc., can be seen in the full listing at the end of this example.
//
// The parent thread creates the `bdlma::ConcurrentPoolAllocator` and work
// queue by the statements:
// ```
// bdlma::ConcurrentPoolAllocator poolAlloc;
// my1_WorkQueue queue(&poolAlloc);
// ```
// Note that since the default constructor is used to create `poolAlloc`, the
// pooled size has not yet been fixed.
//
// The work queue is defined by the following data structures.
// ```
// struct my1_WorkItem {
//     // DATA
//     char *d_item;  // represents work to perform
// };
//
// struct my1_WorkQueue {
//     // DATA
//     bsl::list<my1_WorkItem>  d_queue;    // queue of work requests
//     bslmt::Mutex             d_mx;       // protects the shared queue
//     bslmt::Condition         d_cv;       // signals existence of new work
//     bslma::Allocator        *d_alloc_p;  // pooled allocator
//
//     // CREATORS
//     explicit my1_WorkQueue(bslma::Allocator *basicAllocator = 0)
//     : d_alloc_p(bslma::Default::allocator(basicAllocator))
//     {
//     }
// };
// ```
// The producer and consumer threads are given the address of the work queue as
// their sole argument.  Here, the producer allocates a work item, initializes
// it with a sequence number in ASCII, enqueues it, and signals its presence to
// the consumer thread.  This action is done 50 times, and then a 51st, empty
// work item is added to inform the consumer of the end of the queue.  The
// first allocation of a work item (100 bytes) fixes the pooled size.  Each
// subsequent allocation is that same size (100 bytes).  The producer's actions
// are shown below:
// ```
// extern "C"
// void *my1_producer(void *arg)
// {
//     my1_WorkQueue *queue = (my1_WorkQueue *)arg;
//
//     for (int i = 0; i < 50; ++i) {
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         int len = static_cast<int>(bsl::strlen(b));
//
//         my1_WorkItem request;
//
//         // Fixed allocation size sufficient for content.
//
//         request.d_item = (char *)queue->d_alloc_p->allocate(100);
//
//         bsl::memcpy(request.d_item, b, len+1);
//
//         // Enqueue item and signal any waiting threads.
//         // ...
//     }
//
//     // Add empty item.
//     // ...
//
//     return queue;
// }
// ```
// When the consumer thread finds that the queue is not empty it dequeues the
// item, verifies its content (a sequence number in ASCII), returns the work
// item to the pool, and checks for the next item.  If the queue is empty, the
// consumer blocks until signaled by the producer.  An empty work item
// indicates that the producer will send no more items, so the consumer exits.
// The consumer's actions are shown below:
// ```
// extern "C"
// void *my1_consumer(void *arg)
// {
//     my1_WorkQueue *queue = (my1_WorkQueue *)arg;
//
//     for (int i = 0; ; ++i) {
//
//         // Block until work item on queue.
//         // ...
//
//         // Dequeue item.
//         // ...
//
//         // Break when end-of-work item received.
//         // ...
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         assert(bsl::strcmp(b, item.d_item) == 0);   // check content
//
//         queue->d_alloc_p->deallocate(item.d_item);  // deallocate
//     }
//
//     return 0;
// }
// ```
// A complete listing of the example's structures and functions follows:
// ```
// struct my1_WorkItem {
//     // DATA
//     char *d_item;  // represents work to perform
// };
//
// struct my1_WorkQueue {
//     // DATA
//     bsl::list<my1_WorkItem>  d_queue;    // queue of work requests
//     bslmt::Mutex             d_mx;       // protects the shared queue
//     bslmt::Condition         d_cv;       // signals existence of new work
//     bslma::Allocator        *d_alloc_p;  // pooled allocator
//
//   private:
//     // Not implemented:
//     my1_WorkQueue(const my1_WorkQueue&);
//
//   public:
//     // CREATORS
//     explicit my1_WorkQueue(bslma::Allocator *basicAllocator = 0)
//     : d_alloc_p(bslma::Default::allocator(basicAllocator))
//     {
//     }
// };
//
// extern "C" void *my1_producer(void *arg)
// {
//     my1_WorkQueue *queue = (my1_WorkQueue *)arg;
//
//     for (int i = 0; i < 50; ++i) {
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         int len = static_cast<int>(bsl::strlen(b));
//
//         my1_WorkItem request;
//         request.d_item = (char *)queue->d_alloc_p->allocate(100);
//         bsl::memcpy(request.d_item, b, len+1);
//
//         if (veryVerbose) {
//             // Assume thread-safe implementations of 'cout' and 'endl'
//             // exist (named 'MTCOUT' and 'MTENDL', respectively).
//
//             MTCOUT << "Enqueuing " << request.d_item << MTENDL;
//         }
//
//         queue->d_mx.lock();
//         queue->d_queue.push_back(request);
//         queue->d_mx.unlock();
//         queue->d_cv.signal();
//     }
//
//     my1_WorkItem request;
//     request.d_item = 0;
//
//     queue->d_mx.lock();
//     queue->d_queue.push_back(request);
//     queue->d_mx.unlock();
//     queue->d_cv.signal();
//
//     return queue;
// }
//
// extern "C" void *my1_consumer(void *arg)
// {
//     my1_WorkQueue *queue = (my1_WorkQueue *)arg;
//
//     for (int i = 0; ; ++i) {
//
//         queue->d_mx.lock();
//         while (0 == queue->d_queue.size()) {
//             queue->d_cv.wait(&queue->d_mx);
//         }
//
//         my1_WorkItem item = queue->d_queue.front();
//         queue->d_queue.pop_front();
//         queue->d_mx.unlock();
//
//         if (0 == item.d_item) {
//             break;
//         }
//
//         // Process the work requests.
//         if (veryVerbose) {
//             // Assume thread-safe implementations of 'cout' and 'endl'
//             // exist (named 'MTCOUT' and 'MTENDL', respectively).
//
//             MTCOUT << "Processing " << item.d_item << MTENDL;
//         }
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         assert(bsl::strcmp(b, item.d_item) == 0);
//
//         queue->d_alloc_p->deallocate(item.d_item);
//     }
//
//     return 0;
// }
// ```
// In the application `main`:
// ```
// {
//     bdlma::ConcurrentPoolAllocator poolAlloc;
//     my1_WorkQueue queue(&poolAlloc);
//
//     bslmt::ThreadAttributes attributes;
//
//     bslmt::ThreadUtil::Handle producerHandle;
//     int status = bslmt::ThreadUtil::create(&producerHandle,
//                                            attributes,
//                                            &my1_producer,
//                                            &queue);
//     assert(0 == status);
//
//     bslmt::ThreadUtil::Handle consumerHandle;
//     status = bslmt::ThreadUtil::create(&consumerHandle,
//                                        attributes,
//                                        &my1_consumer,
//                                        &queue);
//     assert(0 == status);
//     status = bslmt::ThreadUtil::join(consumerHandle);
//     assert(0 == status);
//     status = bslmt::ThreadUtil::join(producerHandle);
//     assert(0 == status);
// }
// ```
//
///Example 2: Variable Allocation Size
///- - - - - - - - - - - - - - - - - -
// The following example illustrates the use of
// `bdlma::ConcurrentPoolAllocator` when allocations are of varying size.  A
// `bdlma::ConcurrentPoolAllocator` is used in the implementation of a "work
// queue" where each "item" enqueued by a producer thread varies in size, but
// all items are smaller than a known maximum.  Concurrently, a consumer thread
// dequeues each work item when it is available, verifies its content (a
// sequence number in ASCII), and deallocates the work item.  The concurrent
// allocations and deallocations are valid because
// `bdlma::ConcurrentPoolAllocator` is thread-safe.
//
// First, an abstract of the example will be given with focus and commentary on
// the relevant details of `bdlma::ConcurrentPoolAllocator`.  Details
// pertaining to queue management, thread creation, thread synchronization,
// etc., can be seen in the full listing at the end of this example.
//
// The parent thread creates the `bdlma::ConcurrentPoolAllocator` and work
// queue by the statements:
// ```
// bdlma::ConcurrentPoolAllocator poolAlloc(100);
// my1_WorkQueue queue(&poolAlloc);
// ```
// Note that the pooled size (100) is specified in the construction of
// `poolAlloc`.  Any requests in excess of that size will be satisfied by
// implicit calls to the default allocator, not from the underlying pool.
//
// The work queue is defined by the following data structures.
// ```
// struct my2_WorkItem {
//     // DATA
//     char *d_item;  // represents work to perform
// };
//
// struct my2_WorkQueue {
//     // DATA
//     bsl::list<my2_WorkItem>  d_queue;    // queue of work requests
//     bslmt::Mutex             d_mx;       // protects the shared queue
//     bslmt::Condition         d_cv;       // signals existence of new work
//     bslma::Allocator        *d_alloc_p;  // pooled allocator
//
//     // CREATORS
//     explicit my2_WorkQueue(bslma::Allocator *basicAllocator = 0)
//     : d_queue(basic_Allocator)
//     , d_alloc_p(bslma::Default::allocator(basic_Allocator))
//     {
//     }
// };
// ```
// In this example (unlike Example 1), the given allocator is used not only for
// the work items, but is also passed to the constructor of `d_queue` so that
// it also serves memory for the operations of `bsl::list<my2_WorkItem>`.
//
// The producer and consumer threads are given the address of the work queue as
// their sole argument.  Here, the producer allocates a work item, initializes
// it with a sequence number in ASCII, enqueues it, and signals its presence to
// the consumer thread.  The action is done 50 times, and then a 51st, empty
// work item is added to inform the consumer of the end of the queue.  In this
// example, each work item is sized to match the length of its contents, the
// sequence number in ASCII.  The producer's actions are shown below:
// ```
// extern "C" void *my2_producer(void *arg)
// {
//     my2_WorkQueue *queue = (my2_WorkQueue *)arg;
//
//     for (int i = 0; i < 50; ++i) {
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         int len = static_cast<int>(bsl::strlen(b));
//
//         my2_WorkItem request;
//
//         // Allocate item to exactly match space needed for content.
//
//         request.d_item = (char *)queue->d_alloc_p->allocate(len+1);
//
//         bsl::memcpy(request.d_item, b, len+1);
//
//         // Enqueue item and signal any waiting threads.
//         // ...
//     }
//
//     // Add empty item.
//     // ...
//
//     return queue;
// }
// ```
// The actions of this consumer thread are essentially the same as those of the
// consumer thread in Example 1.
//
// When the consumer thread finds that the queue is not empty, it dequeues the
// item, verifies its content (a sequence number in ASCII), returns the work
// item to the pool, and checks for the next item.  If the queue is empty, the
// consumer blocks until signaled by the producer.  An empty work item
// indicates that the producer will send no more items, so the consumer exits.
// The consumer's actions are shown below.
// ```
// extern "C" void *my2_consumer(void *arg)
// {
//     my2_WorkQueue *queue = (my2_WorkQueue *)arg;
//
//     while (int i = 0; ; ++i) {
//
//         // Block until work item on queue.
//         // ...
//
//         // Deque item.
//         // ...
//
//         // Break when end-of-work item received.
//         // ...
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         assert(bsl::strcmp(b, item.d_item) == 0);   // verify content
//
//         queue->d_alloc_p->deallocate(item.d_item);  // deallocate
//     }
//
//     return 0;
// }
// ```
// A complete listing of the example's structures and functions follows:
// ```
// struct my2_WorkItem {
//     // DATA
//     char *d_item;  // represents work to perform
// };
//
// struct my2_WorkQueue {
//     // DATA
//     bsl::list<my2_WorkItem>  d_queue;    // queue of work requests
//     bslmt::Mutex             d_mx;       // protects the shared queue
//     bslmt::Condition         d_cv;       // signals existence of new work
//     bslma::Allocator        *d_alloc_p;  // pooled allocator
//
//   private:
//     // Not implemented:
//     my2_WorkQueue(const my2_WorkQueue&);
//
//   public:
//     // CREATORS
//     explicit my2_WorkQueue(bslma::Allocator *basicAllocator = 0)
//     : d_queue(basicAllocator)
//     , d_alloc_p(basicAllocator)
//     {
//     }
// };
//
// extern "C" void *my2_producer(void *arg)
// {
//     my2_WorkQueue *queue = (my2_WorkQueue *)arg;
//
//     for (int i = 0; i < 50; ++i) {
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         int len = static_cast<int>(bsl::strlen(b));
//
//         my2_WorkItem request;
//         request.d_item = (char *)queue->d_alloc_p->allocate(len+1);
//         bsl::memcpy(request.d_item, b, len+1);
//
//         if (veryVerbose) {
//             // Assume thread-safe implementations of 'cout' and 'endl'
//             // exist (named 'MTCOUT' and 'MTENDL', respectively).
//
//             MTCOUT << "Enqueuing " << request.d_item << MTENDL;
//         }
//
//         queue->d_mx.lock();
//         queue->d_queue.push_back(request);
//         queue->d_mx.unlock();
//         queue->d_cv.signal();
//     }
//
//     my2_WorkItem request;
//     request.d_item = 0;
//
//     queue->d_mx.lock();
//     queue->d_queue.push_back(request);
//     queue->d_mx.unlock();
//     queue->d_cv.signal();
//
//     return queue;
// }
//
// extern "C" void *my2_consumer(void *arg)
// {
//     my2_WorkQueue *queue = (my2_WorkQueue *)arg;
//
//     for (int i = 0; ; ++i) {
//
//         queue->d_mx.lock();
//         while (0 == queue->d_queue.size()) {
//             queue->d_cv.wait(&queue->d_mx);
//         }
//
//         my2_WorkItem item = queue->d_queue.front();
//         queue->d_queue.pop_front();
//         queue->d_mx.unlock();
//
//         if (0 == item.d_item) {
//             break;
//         }
//
//         // Process the work requests.
//         if (veryVerbose) {
//             // Assume thread-safe implementations of 'cout' and 'endl'
//             // exist (named 'MTCOUT' and 'MTENDL', respectively).
//
//             MTCOUT << "Processing " << item.d_item << MTENDL;
//         }
//
//         char b[100];
//         bsl::snprintf(b, sizeof b, "%d", i);
//         assert(bsl::strcmp(b, item.d_item) == 0);
//
//         queue->d_alloc_p->deallocate(item.d_item);
//     }
//
//     return 0;
// }
// ```
// In the application's `main`:
// ```
// {
//     bdlma::ConcurrentPoolAllocator poolAlloc(100);
//     my2_WorkQueue queue(&poolAlloc);
//
//     bslmt::ThreadAttributes attributes;
//
//     bslmt::ThreadUtil::Handle producerHandle;
//     int status = bslmt::ThreadUtil::create(&producerHandle,
//                                           attributes,
//                                           &my2_producer,
//                                           &queue);
//     assert(0 == status);
//
//     bslmt::ThreadUtil::Handle consumerHandle;
//     status = bslmt::ThreadUtil::create(&consumerHandle,
//                                       attributes,
//                                       &my2_consumer,
//                                       &queue);
//     assert(0 == status);
//     status = bslmt::ThreadUtil::join(consumerHandle);
//     assert(0 == status);
//     status = bslmt::ThreadUtil::join(producerHandle);
//     assert(0 == status);
// }
// ```

#include <bdlscm_version.h>

#include <bdlma_concurrentpool.h>

#include <bslma_allocator.h>

#include <bsls_alignmentutil.h>
#include <bsls_atomic.h>
#include <bsls_blockgrowth.h>
#include <bsls_keyword.h>
#include <bsls_objectbuffer.h>
#include <bsls_types.h>

namespace BloombergLP {
namespace bdlma {

                      // =============================
                      // class ConcurrentPoolAllocator
                      // =============================

/// This class implements the `bslma::Allocator` protocol to provide an
/// allocator that manages pooled memory blocks of some uniform size,
/// specified either at construction, or at the first invocation of the
/// `allocate` method.  This allocator maintains an internal linked list of
/// free memory blocks, and dispenses one block for each `allocate` method
/// invocation.  When a memory block is deallocated, it is returned to the
/// free list for potential reuse.
///
/// This class guarantees thread-safety while allocating or releasing
/// memory.
class ConcurrentPoolAllocator : public bslma::Allocator {

  public:
    // PUBLIC TYPES
    typedef bsls::Types::size_type size_type;  // type for block size

  private:
    // PRIVATE TYPES
    enum {
        k_MAGIC_NUMBER  = 0x111902,  // magic number that is inserted in header
                                     // of items that are allocated from the
                                     // underlying pool

        k_UNINITIALIZED =  0,        // pool not yet initialized

        k_INITIALIZED   =  1,        // pool is initialized

        k_INITIALIZING  = -1         // pool initialization in progress
    };

    /// Leading header on each allocated memory block.  If the memory block
    /// is allocated from the pool, `d_magicNumber` is set to
    /// `k_MAGIC_NUMBER`.  Otherwise, memory is allocated from the external
    /// allocator supplied at construction, and `d_magicNumber` is set to 0.
    union Header {

        int                                 d_magicNumber; // allocation source
                                                           // and sanity check

        bsls::AlignmentUtil::MaxAlignedType d_dummy;       // forces alignment
    };

    // DATA
    bsls::AtomicInt   d_initialized;  // initialization state indicator

    bsls::ObjectBuffer<ConcurrentPool>
                      d_pool;         // buffer used to hold the 'Pool'
                                      // (initialization occurs when the pooled
                                      // memory block size first becomes known)

    size_type         d_blockSize;    // block size

    bsls::BlockGrowth::Strategy
                      d_growthStrategy;
                                      // growth strategy of the chunk size

    int               d_maxBlocksPerChunk;
                                      // max chunk size (in blocks-per-chunk)

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

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

  public:
    // CREATORS

    /// Create a pool allocator that returns blocks of contiguous memory of
    /// uniform size for each `allocate` method invocation, where the size
    /// is determined by the first invocation of `allocate`.  Optionally
    /// specify a `growthStrategy` used to control the growth of internal
    /// memory chunks (from which memory blocks are dispensed).  If
    /// `growthStrategy` is not specified, geometric growth is used.  If
    /// `growthStrategy` is specified, optionally specify a
    /// `maxBlocksPerChunk`, indicating the maximum number of blocks to be
    /// allocated at once when the underlying pool must be replenished.  If
    /// `maxBlocksPerChunk` is not specified, an implementation-defined
    /// value is used.  If geometric growth is used, the chunk size grows
    /// starting at the value returned by `blockSize`, doubling in size
    /// until the size is exactly `blockSize() * maxBlocksPerChunk`.  If
    /// constant growth is used, the chunk size is always
    /// `maxBlocksPerChunk`.  Optionally specify a `basicAllocator` used to
    /// supply memory.  If `basicAllocator` is 0, the currently installed
    /// default allocator is used.  The behavior is undefined unless
    /// `1 <= maxBlocksPerChunk`.
    explicit
    ConcurrentPoolAllocator(bslma::Allocator *basicAllocator = 0);
    explicit
    ConcurrentPoolAllocator(bsls::BlockGrowth::Strategy  growthStrategy,
                            bslma::Allocator            *basicAllocator = 0);
    ConcurrentPoolAllocator(bsls::BlockGrowth::Strategy  growthStrategy,
                            int                          maxBlocksPerChunk,
                            bslma::Allocator            *basicAllocator = 0);

    /// Create a pool allocator that returns blocks of contiguous memory of
    /// the specified `blockSize` (in bytes) for each `allocate` method
    /// invocation.  Optionally specify a `growthStrategy` used to control
    /// the growth of internal memory chunks (from which memory blocks are
    /// dispensed).  If `growthStrategy` is not specified, geometric growth
    /// is used.  If `blockSize` and `growthStrategy` are specified,
    /// optionally specify a `maxBlocksPerChunk`, indicating the maximum
    /// number of blocks to be allocated at once when the underlying pool
    /// must be replenished.  If `maxBlocksPerChunk` is not specified, an
    /// implementation-defined value is used.  If geometric growth is used,
    /// the chunk size grows starting at `blockSize`, doubling in size until
    /// the size is exactly `blockSize * maxBlocksPerChunk`.  If constant
    /// growth is used, the chunk size is always `maxBlocksPerChunk`.
    /// Optionally specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.  The behavior is undefined unless `1 <= maxBlocksPerChunk`.
    explicit
    ConcurrentPoolAllocator(size_type         blockSize,
                            bslma::Allocator *basicAllocator = 0);
    ConcurrentPoolAllocator(size_type                    blockSize,
                            bsls::BlockGrowth::Strategy  growthStrategy,
                            bslma::Allocator            *basicAllocator = 0);
    ConcurrentPoolAllocator(size_type                    blockSize,
                            bsls::BlockGrowth::Strategy  growthStrategy,
                            int                          maxBlocksPerChunk,
                            bslma::Allocator            *basicAllocator = 0);

    /// Destroy this pool allocator.
    ~ConcurrentPoolAllocator() BSLS_KEYWORD_OVERRIDE;

    // MANIPULATORS

    /// Return a newly allocated block of memory of (at least) the specified
    /// positive `size` (in bytes), and having alignment conforming to the
    /// platform requirement for any object having `size` bytes.  If `size`
    /// is 0, a null pointer is returned with no effect.  If the block size
    /// is not supplied at construction, then the `size` specified in the
    /// first call to this method fixes the block size.  If `size` is
    /// greater than the value returned by `blockSize`, the allocation
    /// request will be satisfied directly by the external allocator
    /// supplied at construction (or the default allocator, if no allocator
    /// was supplied).
    void *allocate(size_type size) BSLS_KEYWORD_OVERRIDE;

    /// Return the memory at the specified `address` back to this allocator.
    /// If `address` is 0, this method has no effect.  The behavior is
    /// undefined unless `address` was allocated using this allocator and
    /// has not already been deallocated.
    void deallocate(void *address) BSLS_KEYWORD_OVERRIDE;

    /// Relinquish all memory currently allocated through this pool
    /// allocator.
    void release();

    /// Reserve memory from this pool allocator to satisfy memory requests
    /// for at least the specified `numObjects` before the pool replenishes.
    /// The behavior is undefined unless `0 <= numObjects`.  Note that this
    /// operation has no effect unless block size was supplied at
    /// construction, or `allocate` was invoked.
    void reserveCapacity(int numObjects);

    // ACCESSORS

    /// Return the size (in bytes) of the memory blocks allocated from this
    /// allocator.  Note that all blocks dispensed by this allocator have
    /// the same size.
    size_type blockSize() const;
};

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

                      // -----------------------------
                      // class ConcurrentPoolAllocator
                      // -----------------------------

// MANIPULATORS
inline
void ConcurrentPoolAllocator::release()
{
    if (d_initialized == k_INITIALIZED) {
        d_pool.object().release();
    }
}

inline
void ConcurrentPoolAllocator::reserveCapacity(int numObjects)
{
    if (d_initialized == k_INITIALIZED) {
        d_pool.object().reserveCapacity(numObjects);
    }
}

// ACCESSORS
inline
bsls::Types::size_type ConcurrentPoolAllocator::blockSize() const
{
    return d_blockSize;
}

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