// bdlma_concurrentmultipool.h                                        -*-C++-*-

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

#ifndef INCLUDED_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:
//:   o the unique growth strategy for all pools, or
//:   o (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:
//:     o the unique maximum-blocks-per-chunk value for all of the pools, or
//:     o 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:
//      // 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';
//      }
//
//      // 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';
//      }
//
//      // 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';
//      }
//
//      // 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)
//      {
//      }
//
//      // 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
                        // =========================

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.

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

        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
    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);
        // 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.

    // PRIVATE ACCESSORS
    int findPool(bsls::Types::size_type size) const;
        // 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.

  public:
    // CREATORS
    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.  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).

    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);
        // 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();
        // Destroy this multipool.  All memory allocated from this memory pool
        // is released.

    // MANIPULATORS
    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 > 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 deallocate(void *address);
        // 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.

    template <class TYPE>
    void deleteObject(const TYPE *object);
        // 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 deleteObjectRaw(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.

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

    void reserveCapacity(bsls::Types::size_type size, int numBlocks);
        // 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'.

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

    bsls::Types::size_type maxPooledBlockSize() 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.


                                  // Aspects

    bslma::Allocator *allocator() const;
        // 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.
};

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