// bdlma_concurrentmultipool.h                                        -*-C++-*-
#ifndef INCLUDED_BDLMA_CONCURRENTMULTIPOOL
#define INCLUDED_BDLMA_CONCURRENTMULTIPOOL

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

//@PURPOSE: Provide a memory manager to manage pools of varying block sizes.
//
//@CLASSES:
//   bdlma::ConcurrentMultipool: memory manager that manages pools of blocks
//
//@SEE_ALSO: bdlma_concurrentpool, bdlmca_multipoolallocator
//
//@DESCRIPTION: This component implements a memory manager,
// `bdlma::ConcurrentMultipool`, that maintains a configurable number of
// `bdlma::ConcurrentPool` objects, each dispensing memory blocks of a unique
// size.  The `bdlma::ConcurrentPool` objects are placed in an array, starting
// at index 0, with each successive pool managing memory blocks of a size twice
// that of the previous pool.  Each multipool allocation (deallocation) request
// allocates memory from (returns memory to) the internal pool managing memory
// blocks of the smallest size not less than the requested size, or else from a
// separately managed list of memory blocks, if no internal pool managing
// memory block of sufficient size exists.  Both the `release` method and the
// destructor of a `bdlma::ConcurrentMultipool` release all memory currently
// allocated via the object.
//
// A `bdlma::ConcurrentMultipool` can be depicted visually:
// ```
//                   +-----+--- memory blocks of 8 bytes
//                   |     |
//  ========       ----- ----- ------------
// |8 bytes |---->|     |     |     ...    |
// >========<      =====^=====^============
// |16 bytes|
// >========<      \___________ __________/
// |32 bytes|                  V
// >========<              a "chunk"
// |        |
// |  ...   |
// |        |
//  ========
//     |
//     +------- array of 'bdlma::ConcurrentPool'
// ```
// Note that a "chunk" is a large, contiguous block of memory, internal to a
// `bdlma::ConcurrentPool` maintained by the multipool, from which memory
// blocks of uniform size are dispensed to users.
//
///Thread Safety
///-------------
// `bdlma::ConcurrentMultipool` is *fully thread-safe*, meaning any operation
// on the same object can be safely invoked from any thread.
//
///Configuration at Construction
///-----------------------------
// When creating a `bdlma::ConcurrentMultipool`, clients can optionally
// configure:
//
// 1. NUMBER OF POOLS -- the number of internal pools (the block size managed
//    by the first pool is eight bytes, with each successive pool managing
//    block of a size twice that of the previous pool).
// 2. GROWTH STRATEGY -- geometrically growing chunk size starting from 1 (in
//    terms of the number of memory blocks per chunk), or fixed chunk size,
//    specified as either:
//   - the unique growth strategy for all pools, or
//   - (if the number of pools is specified) an array of growth strategies
//     corresponding to each individual pool
//   If the growth strategy is not specified, geometric growth is used for all
//   pools.
// 3. MAX BLOCKS PER CHUNK -- the maximum number of memory blocks within a
//    chunk, specified as either:
//     - the unique maximum-blocks-per-chunk value for all of the pools, or
//     - an array of maximum-blocks-per-chunk values corresponding to each
//       individual pool.
//   If the maximum blocks per chunk is not specified, an
//   implementation-defined default value is used.  Note that the maximum
//   blocks per chunk can be configured only if the number of pools is also
//   configured.
// 4. BASIC ALLOCATOR -- the allocator used to supply memory (to replenish an
//    internal pool, or directly if the maximum block size is exceeded).  If
//    not specified, the currently installed default allocator (see
//    `bslma_default`) is used.
//
// A default-constructed multipool has a relatively small,
// implementation-defined number of pools `N` with respective block sizes
// ranging from `2^3 = 8` to `2^(N+2)`.  By default, the initial chunk size,
// (i.e., the number of blocks of a given size allocated at once to replenish a
// pool's memory) is 1, and each pool's chunk size grows geometrically until it
// reaches an implementation-defined maximum, at which it is capped.  Finally,
// unless otherwise specified, all memory comes from the allocator that was the
// currently installed default allocator at the time the
// `bdlma::ConcurrentMultipool` was created.
//
// Using the various pooling options described above, we can configure the
// number of pools maintained, whether replenishment should be adaptive (i.e.,
// geometric starting with 1) or fixed at a maximum chunk size, what that
// maximum chunk size should be (which need not be an integral power of 2), and
// the underlying allocator used to supply memory.  Note that both GROWTH
// STRATEGY and MAX BLOCKS PER CHUNK can be specified separately either as a
// single value applying to all of the maintained pools, or as an array of
// values, with the elements applying to each individually maintained pool.
//
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using a `bdlma::ConcurrentMultipool` Directly
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A `bdlma::ConcurrentMultipool` can be used by containers that hold different
// types of elements, each of uniform size, for efficient memory allocation of
// new elements.  Suppose we have a factory class, `my_MessageFactory`, that
// creates messages based on user requests.  Each message is created with the
// most efficient memory storage possible - using predefined 8-byte, 16-byte
// and 32-byte buffers.  If the message size exceeds the three predefined
// values, a generic message is used.  For efficient memory allocation of
// messages, we use a `bdlma::ConcurrentMultipool`.
//
// First, we define our message types as follows:
// ```
// class my_MessageFactory;
//
// class my_Message {
//     // This class represents a general message interface that provides a
//     // 'getMessage' method for clients to retrieve the underlying message.
//
//   public:
//     // CREATORS
//     virtual ~my_Message() {}
//         // Destroy this object.
//
//     // ACCESSORS
//     virtual const char *getMessage() = 0;
//         // Return the null-terminated message string.
// };
//
// class my_SmallMessage : public my_Message {
//     // This class represents an 8-byte message (including null terminator).
//
//     // DATA
//     char d_buffer[8];
//
//     // FRIEND
//     friend class my_MessageFactory;
//
//     // NOT IMPLEMENTED
//     my_SmallMessage(const my_SmallMessage&);
//     my_SmallMessage& operator=(const my_SmallMessage&);
//
//     // PRIVATE CREATORS
//     my_SmallMessage(const char *msg, int length)
//     {
//         assert(length <= 7);
//
//         bsl::memcpy(d_buffer, msg, length);
//         d_buffer[length] = '\0';
//     }
//
//     virtual ~my_SmallMessage() {}
//         // Destroy this object.
//
//     // PRIVATE ACCESSORS
//     virtual const char *getMessage()
//     {
//         return d_buffer;
//     }
// };
//
// class my_MediumMessage : public my_Message {
//     // This class represents a 16-byte message (including null
//     // terminator).
//
//     // DATA
//     char d_buffer[16];
//
//     // FRIEND
//     friend class my_MessageFactory;
//
//     // NOT IMPLEMENTED
//     my_MediumMessage(const my_MediumMessage&);
//     my_MediumMessage& operator=(const my_MediumMessage&);
//
//     // PRIVATE CREATORS
//     my_MediumMessage(const char *msg, int length)
//     {
//         assert(length <= 15);
//
//         bsl::memcpy(d_buffer, msg, length);
//         d_buffer[length] = '\0';
//     }
//
//     virtual ~my_MediumMessage() {}
//         // Destroy this object.
//
//     // PRIVATE ACCESSORS
//     virtual const char *getMessage()
//     {
//         return d_buffer;
//     }
// };
//
// class my_LargeMessage : public my_Message {
//     // This class represents a 32-byte message (including null
//     // terminator).
//
//     // DATA
//     char d_buffer[32];
//
//     // FRIEND
//     friend class my_MessageFactory;
//
//     // NOT IMPLEMENTED
//     my_LargeMessage(const my_LargeMessage&);
//     my_LargeMessage& operator=(const my_LargeMessage&);
//
//     // PRIVATE CREATORS
//     my_LargeMessage(const char *msg, int length)
//     {
//         assert(length <= 31);
//
//         bsl::memcpy(d_buffer, msg, length);
//         d_buffer[length] = '\0';
//     }
//
//     virtual ~my_LargeMessage() {}
//         // Destroy this object.
//
//     // PRIVATE ACCESSORS
//     virtual const char *getMessage()
//     {
//         return d_buffer;
//     }
// };
//
// class my_GenericMessage : public my_Message {
//     // This class represents a generic message.
//
//     // DATA
//     char *d_buffer;
//
//     // FRIEND
//     friend class my_MessageFactory;
//
//     // NOT IMPLEMENTED
//     my_GenericMessage(const my_GenericMessage&);
//     my_GenericMessage& operator=(const my_GenericMessage&);
//
//     // PRIVATE CREATORS
//     my_GenericMessage(char *msg) : d_buffer(msg)
//     {
//     }
//
//     virtual ~my_GenericMessage() {}
//         // Destroy this object.
//
//     // PRIVATE ACCESSORS
//     virtual const char *getMessage()
//     {
//         return d_buffer;
//     }
// };
// ```
// Then we define our factory class, `my_MessageFactory`, as follows:
// ```
// class my_MessageFactory {
//     // This class implements an efficient message factory that builds and
//     // returns messages.  The life-time of the messages created by this
//     // factory is the same as this factory.
//
//     // DATA
//     bdlma::ConcurrentMultipool d_multipool;  // multipool used to supply
//                                              // memory
//
//   private:
//     // Not implemented:
//     my_MessageFactory(const my_MessageFactory&);
//
//   public:
//     // CREATORS
//     my_MessageFactory(bslma::Allocator *basicAllocator = 0);
//         // Create a message factory.  Optionally specify a 'basicAllocator'
//         // used to supply memory.  If 'basicAllocator' is 0, the currently
//         // installed default allocator is used.
//
//     ~my_MessageFactory();
//         // Destroy this factory and reclaim all messages created by it.
//
//     // MANIPULATORS
//     my_Message *createMessage(const char *data);
//         // Create a message storing the specified 'data'.  The behavior is
//         // undefined unless 'data' is null-terminated.
//
//     void disposeAllMessages();
//         // Dispose of all created messages.
//
//     void disposeMessage(my_Message *message);
//         // Dispose of the specified 'message'.  The behavior is undefined
//         // unless 'message' was created by this factory.
// };
// ```
// The use of a multipool and the `release` method enables the
// `disposeAllMessages` method to quickly deallocate all memory blocks used to
// create messages:
// ```
// // MANIPULATORS
// inline
// void my_MessageFactory::disposeAllMessages()
// {
//     d_multipool.release();
// }
// ```
// The multipool can also reuse deallocated memory.  Once a message is
// destroyed by the `disposeMessage` method, memory allocated for that message
// is reclaimed by the multipool and can be used to create the next message
// having the same size:
// ```
// inline
// void my_MessageFactory::disposeMessage(my_Message *message)
// {
//     d_multipool.deleteObject(message);
// }
// ```
// A multipool optimizes the allocation of memory by using
// dynamically-allocated buffers (also known as chunks) to supply memory.  As
// each chunk can satisfy multiple memory block requests before requiring
// additional dynamic memory allocation, the number of dynamic allocation
// requests needed is greatly reduced.
//
// For the number of pools managed by the multipool, we chose to use the
// implementation-defined default value instead of calculating and specifying a
// value.  Note that if users want to specify the number of pools, the value
// can be calculated as the smallest `N` such that the following relationship
// holds:
// ```
// N > log2(sizeof(Object Type)) - 2
// ```
// Continuing on with the usage example:
// ```
// // CREATORS
// my_MessageFactory::my_MessageFactory(bslma::Allocator *basicAllocator)
// : d_multipool(basicAllocator)
// {
// }
// ```
// Note that in the destructor, all outstanding messages are reclaimed
// automatically when `d_multipool` is destroyed:
// ```
// my_MessageFactory::~my_MessageFactory()
// {
// }
// ```
// A `bdlma::ConcurrentMultipool` is ideal for allocating the different sized
// messages since repeated deallocations might be necessary (which renders a
// `bdlma::SequentialPool` unsuitable) and the sizes of these types are all
// different:
// ```
// // MANIPULATORS
// my_Message *my_MessageFactory::createMessage(const char *data)
// {
//     enum { k_SMALL = 8, k_MEDIUM = 16, k_LARGE = 32 };
//
//     const int length = static_cast<int>(bsl::strlen(data));
//
//     if (length < k_SMALL) {
//         return new(d_multipool.allocate(sizeof(my_SmallMessage)))
//                                   my_SmallMessage(data, length);  // RETURN
//     }
//
//     if (length < k_MEDIUM) {
//         return new(d_multipool.allocate(sizeof(my_MediumMessage)))
//                                  my_MediumMessage(data, length);  // RETURN
//     }
//
//     if (length < k_LARGE) {
//         return new(d_multipool.allocate(sizeof(my_LargeMessage)))
//                                   my_LargeMessage(data, length);  // RETURN
//     }
//
//     char *buffer = (char *)d_multipool.allocate(length + 1);
//     bsl::memcpy(buffer, data, length + 1);
//
//     return new(d_multipool.allocate(sizeof(my_GenericMessage)))
//                                                  my_GenericMessage(buffer);
// }
// ```
//
///Example 2: Implementing an Allocator Using `bdlma::ConcurrentMultipool`
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// `bslma::Allocator` is used throughout the interfaces of BDE components.
// Suppose we would like to create a multipool allocator,
// `my_MultipoolAllocator`, that allocates memory from multiple
// `bdlma::ConcurrentPool` objects in a similar fashion to
// `bdlma::ConcurrentMultipool`.  This class can be used directly to implement
// such an allocator.
//
// Note that the documentation for this class is simplified for this usage
// example.  Please see `bdlmca_multipoolallocator` for full documentation of a
// similar class.
// ```
// class my_MultipoolAllocator : public bslma::Allocator{
//     // This class implements the 'bslma::Allocator' protocol to provide an
//     // allocator that manages a set of memory pools, each dispensing memory
//     // blocks of a unique size, with each successive pool's block size
//     // being twice that of the previous one.
//
//     // DATA
//     bdlma::ConcurrentMultipool d_multiPool;  // memory manager for
//                                              // allocated memory blocks
//
//   public:
//     // CREATORS
//     my_MultipoolAllocator(bslma::Allocator *basicAllocator = 0);
//         // Create a multipool allocator.  Optionally specify a
//         // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
//         // 0, the currently installed default allocator is used.
//
//     // ...
//
//     virtual ~my_MultipoolAllocator();
//         // Destroy this multipool allocator.  All memory allocated from
//         // this memory pool is released.
//
//     // MANIPULATORS
//     virtual void *allocate(bsls::Types::size_type size);
//         // Return the address of a contiguous block of maximally-aligned
//         // memory of (at least) the specified 'size' (in bytes).  If 'size'
//         // is 0, no memory is allocated and 0 is returned.
//
//     virtual void deallocate(void *address);
//         // Relinquish the memory block at the specified 'address' back to
//         // this multipool allocator for reuse.  The behavior is undefined
//         // unless 'address' is non-zero, was allocated by this multipool
//         // allocator, and has not already been deallocated.
// };
//
// // CREATORS
// inline
// my_MultipoolAllocator::my_MultipoolAllocator(
//                                           bslma::Allocator *basicAllocator)
// : d_multiPool(basicAllocator)
// {
// }
//
// my_MultipoolAllocator::~my_MultipoolAllocator()
// {
// }
//
// // MANIPULATORS
// inline
// void *my_MultipoolAllocator::allocate(bsls::Types::size_type size)
// {
//     return d_multiPool.allocate(size);
// }
//
// inline
// void my_MultipoolAllocator::deallocate(void *address)
// {
//     d_multiPool.deallocate(address);
// }
// ```

#include <bdlscm_version.h>

#include <bdlma_concurrentallocatoradapter.h>
#include <bdlma_blocklist.h>

#include <bslma_allocator.h>
#include <bslma_deleterhelper.h>

#include <bslmt_mutex.h>

#include <bsls_alignmentutil.h>
#include <bsls_blockgrowth.h>
#include <bsls_types.h>

namespace BloombergLP {
namespace bdlma {

class ConcurrentPool;

                        // =========================
                        // class ConcurrentMultipool
                        // =========================

/// This class implements a memory manager that maintains a configurable
/// number of `bdlma::Pool` objects, each dispensing memory blocks of a
/// unique size.  The `Pool` objects are placed in an array, with each
/// successive pool managing memory blocks of size twice that of the
/// previous pool.  Each multipool allocation (deallocation) request
/// allocates memory from (returns memory to) the internal pool having the
/// smallest block size not less than the requested size, or, if no pool
/// manages memory blocks of sufficient sized, from a separately managed
/// list of memory blocks.  Both the `release` method and the destructor of
/// a `bdema::Multipool` release all memory currently allocated via the
/// object.
class ConcurrentMultipool {

    // PRIVATE TYPES

    /// This `struct` provides header information for each allocated memory
    /// block.  The header stores the index to the pool used for the memory
    /// allocation.
    struct Header {

        union {
            int                    d_poolIdx;  // pool used for this memory
                                               // block

            bsls::AlignmentUtil::MaxAlignedType
                                   d_dummy;    // force maximum alignment
        } d_header;
    };

    // DATA
    ConcurrentPool   *d_pools_p;       // array of memory pools, each
                                       // dispensing fixed-size memory blocks

    int               d_numPools;      // number of memory pools

    bsls::Types::size_type
                      d_maxBlockSize;  // largest memory block size; dispensed
                                       // by the 'd_numPools - 1'th pool;
                                       // always a power of 2

    bdlma::BlockList  d_blockList;     // memory manager for "large" memory
                                       // blocks.

    bslmt::Mutex      d_mutex;         // synchronize data access

    ConcurrentAllocatorAdapter
                      d_allocAdapter;  // thread-safe adapter

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

   private:
    // PRIVATE MANIPULATORS

    /// Initialize this multipool with the specified `growthStrategy[Array]`
    /// and `maxBlocksPerChunk[Array]`.  If an array is used, each
    /// individual `bdlma::Pool` maintained by this multipool is initialized
    /// with the corresponding growth strategy or max blocks per chunk entry
    /// within the array.
    void initialize(bsls::BlockGrowth::Strategy growthStrategy,
                    int                         maxBlocksPerChunk);
    void initialize(const bsls::BlockGrowth::Strategy *growthStrategyArray,
                    int                                maxBlocksPerChunk);
    void initialize(bsls::BlockGrowth::Strategy  growthStrategy,
                    const int                   *maxBlocksPerChunkArray);
    void initialize(const bsls::BlockGrowth::Strategy *growthStrategyArray,
                    const int                         *maxBlocksPerChunkArray);

    // PRIVATE ACCESSORS

    /// Return the index of the memory pool in this multipool for an
    /// allocation request of the specified `size` (in bytes).  Note that
    /// the index of the memory pool managing memory blocks having the
    /// minimum block size is 0.
    int findPool(bsls::Types::size_type size) const;

  public:
    // CREATORS

    /// Create a multipool memory manager.  Optionally specify `numPools`,
    /// indicating the number of internally created `Pool` objects; the
    /// block size of the first pool is 8 bytes, with the block size of each
    /// additional pool successively doubling.  If `numPools` is not
    /// specified, an implementation-defined number of pools `N` -- covering
    /// memory blocks ranging in size from `2^3 = 8` to `2^(N+2)` -- are
    /// created.  Optionally specify a `growthStrategy` indicating whether
    /// the number of blocks allocated at once for every internally created
    /// `Pool` should be either fixed or grow geometrically, starting with
    /// 1.  If `growthStrategy` is not specified, the allocation strategy
    /// for each internally created `Pool` object is geometric, starting
    /// from 1.  If `numPools` is specified, optionally specify a
    /// `maxBlocksPerChunk`, indicating the maximum number of blocks to be
    /// allocated at once when a pool must be replenished.  If
    /// `maxBlocksPerChunk` is not specified, an implementation-defined
    /// value is used.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  Memory allocation (and deallocation) requests
    /// will be satisfied using the internally maintained pool managing
    /// memory blocks of the smallest size not less than the requested size,
    /// or directly from the underlying allocator (supplied at
    /// construction), if no internally pool managing memory block of
    /// sufficient size exists.  The behavior is undefined unless
    /// `1 <= numPools` and `1 <= maxBlocksPerChunk`.  Note that, on
    /// platforms where `8 < bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`,
    /// excess memory may be allocated for pools managing smaller blocks.
    /// Also note that `maxBlocksPerChunk` need not be an integral power of
    /// 2; if geometric growth would exceed the maximum value, the chunk
    /// size is capped at that value).
    explicit ConcurrentMultipool(bslma::Allocator *basicAllocator = 0);
    explicit ConcurrentMultipool(int               numPools,
                                 bslma::Allocator *basicAllocator = 0);
    explicit ConcurrentMultipool(
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bslma::Allocator            *basicAllocator = 0);
    ConcurrentMultipool(int                          numPools,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        bslma::Allocator            *basicAllocator = 0);
    ConcurrentMultipool(int                          numPools,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        int                          maxBlocksPerChunk,
                        bslma::Allocator            *basicAllocator = 0);

    /// Create a multipool memory manager having the specified `numPools`,
    /// indicating the number of internally created `Pool` objects; the
    /// block size of the first pool is 8 bytes, with the block size of each
    /// additional pool successively doubling.  Optionally specify a
    /// `growthStrategy` indicating whether the number of blocks allocated
    /// at once for every internally created `Pool` should be either fixed
    /// or grow geometrically, starting with 1.  If `growthStrategy` is not
    /// specified, optionally specify `growthStrategyArray`, indicating the
    /// strategies for each individual `Pool` created by this object.  If
    /// neither `growthStrategy` nor `growthStrategyArray` are specified,
    /// the allocation strategy for each internally created `Pool` object
    /// will grow geometrically, starting from 1.  Optionally specify a
    /// `maxBlocksPerChunk`, indicating the maximum number of blocks to be
    /// allocated at once when a pool must be replenished.  If
    /// `maxBlocksPerChunk` is not specified, optionally specify
    /// `maxBlocksPerChunkArray`, indicating the maximum number of blocks to
    /// allocate at once for each individually created `Pool` object.  If
    /// neither `maxBlocksPerChunk` nor `maxBlocksPerChunkArray` are
    /// specified, an implementation-defined value is used.  Optionally
    /// specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used.  Memory allocation (and deallocation) requests will be
    /// satisfied using the internally maintained pool managing memory
    /// blocks of the smallest size not less than the requested size, or
    /// directly from the underlying allocator (supplied at construction),
    /// if no internally pool managing memory block of sufficient size
    /// exists.  The behavior is undefined unless `1 <= numPools`,
    /// `growthStrategyArray` has at least `numPools` strategies,
    /// `1 <= maxBlocksPerChunk` and `maxBlocksPerChunkArray` have at least
    /// `numPools` positive values.  Note that, on platforms where
    /// `8 < bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`, excess memory may be
    /// allocated for pools managing smaller blocks.  Also note that the
    /// maximum need not be an integral power of 2; if geometric growth
    /// would exceed a maximum value, the chunk size is capped at that
    /// value).
    ConcurrentMultipool(int                                numPools,
                        const bsls::BlockGrowth::Strategy *growthStrategyArray,
                        bslma::Allocator                  *basicAllocator = 0);
    ConcurrentMultipool(int                                numPools,
                        const bsls::BlockGrowth::Strategy *growthStrategyArray,
                        int                                maxBlocksPerChunk,
                        bslma::Allocator                  *basicAllocator = 0);
    ConcurrentMultipool(int                          numPools,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        const int                   *maxBlocksPerChunkArray,
                        bslma::Allocator            *basicAllocator = 0);
    ConcurrentMultipool(
                     int                                numPools,
                     const bsls::BlockGrowth::Strategy *growthStrategyArray,
                     const int                         *maxBlocksPerChunkArray,
                     bslma::Allocator                  *basicAllocator = 0);

    /// Destroy this multipool.  All memory allocated from this memory pool
    /// is released.
    ~ConcurrentMultipool();

    // MANIPULATORS

    /// Return the address of a contiguous block of maximally-aligned memory
    /// of (at least) the specified `size` (in bytes).  If
    /// `size > maxPooledBlockSize()`, the memory allocation is managed
    /// directly by the underlying allocator, and will not be pooled, but
    /// will be deallocated when the `release` method is called, or when
    /// this object is destroyed.  If `size` is 0, no memory is allocated
    /// and 0 is returned.
    void *allocate(bsls::Types::size_type size);

    /// Relinquish the memory block at the specified `address` back to this
    /// multipool object for reuse.  The behavior is undefined unless
    /// `address` is non-zero, was allocated by this multipool object, and
    /// has not already been deallocated.
    void deallocate(void *address);

    /// Destroy the specified `object` based on its dynamic type and then
    /// use this multipool object to deallocate its memory footprint.  This
    /// method has no effect if `object` is 0.  The behavior is undefined
    /// unless `object`, when cast appropriately to `void *`, was allocated
    /// using this multipool object and has not already been deallocated.
    /// Note that `dynamic_cast<void *>(object)` is applied if `TYPE` is
    /// polymorphic, and `static_cast<void *>(object)` is applied otherwise.
    template <class TYPE>
    void deleteObject(const TYPE *object);

    /// Destroy the specified `object` and then use this multipool to
    /// deallocate its memory footprint.  This method has no effect if
    /// `object` is 0.  The behavior is undefined unless `object` is **not** a
    /// secondary base class pointer (i.e., the address is (numerically) the
    /// same as when it was originally dispensed by this multipool), was
    /// allocated using this multipool, and has not already been
    /// deallocated.
    template <class TYPE>
    void deleteObjectRaw(const TYPE *object);

    /// Relinquish all memory currently allocated via this multipool object.
    void release();

    /// Reserve memory from this multipool to satisfy memory requests for at
    /// least the specified `numBlocks` having the specified `size` (in
    /// bytes) before the pool replenishes.  If `size` is 0, this method has
    /// no effect.  The behavior is undefined unless
    /// `size <= maxPooledBlockSize()` and `0 <= numBlocks`.
    void reserveCapacity(bsls::Types::size_type size, int numBlocks);

    // ACCESSORS

    /// Return the number of pools managed by this multipool object.
    int numPools() const;

    /// Return the maximum size of memory blocks that are pooled by this
    /// multipool object.  Note that the maximum value is defined as:
    /// ```
    /// 2 ^ (numPools + 2)
    /// ```
    /// where `numPools` is either specified at construction, or an
    /// implementation-defined value.
    bsls::Types::size_type maxPooledBlockSize() const;


                                  // Aspects

    /// Return the allocator used by this object to allocate memory.  Note
    /// that this allocator can not be used to deallocate memory
    /// allocated through this pool.
    bslma::Allocator *allocator() const;
};

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

                        // -------------------------
                        // class ConcurrentMultipool
                        // -------------------------

// MANIPULATORS
template <class TYPE>
inline
void ConcurrentMultipool::deleteObject(const TYPE *object)
{
    bslma::DeleterHelper::deleteObject(object, this);
}

template <class TYPE>
inline
void ConcurrentMultipool::deleteObjectRaw(const TYPE *object)
{
    bslma::DeleterHelper::deleteObjectRaw(object, this);
}

// ACCESSORS
inline
int ConcurrentMultipool::numPools() const
{
    return d_numPools;
}

inline
bsls::Types::size_type ConcurrentMultipool::maxPooledBlockSize() const
{
    return d_maxBlockSize;
}

// Aspects

inline
bslma::Allocator *ConcurrentMultipool::allocator() const
{
    return d_blockList.allocator();
}

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

#endif

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