// bdlma_concurrentpool.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_CONCURRENTPOOL
#define INCLUDED_BDLMA_CONCURRENTPOOL

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

//@PURPOSE: Provide thread-safe allocation of memory blocks of uniform size.
//
//@CLASSES:
//   bdlma::ConcurrentPool: thread-safe memory manager that allocates blocks
//
//@SEE_ALSO: bdlma_pool
//
//@DESCRIPTION: This component implements a memory pool,
// 'bdlma::ConcurrentPool', that allocates and manages memory blocks of some
// uniform size specified at construction.  A 'bdlma::ConcurrentPool' object
// 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.
//
// Whenever the linked list of free memory blocks is depleted, the
// 'bdlma::ConcurrentPool' replenishes the list by first allocating a large,
// contiguous "chunk" of memory, then splitting the chunk into multiple memory
// blocks.  A chunk and its constituent memory blocks can be depicted visually:
//..
//     +-----+--- memory blocks of uniform size
//     |     |
//   ----- ----- ------------
//  |     |     |     ...    |
//   =====^=====^============
//
//   \___________ __________/
//               V
//           a "chunk"
//..
// Note that the size of the allocated chunk is determined by both the growth
// strategy and maximum blocks per chunk, either of which can be optionally
// specified at construction (see the "Configuration at Construction" section).
//
///Configuration at Construction
///-----------------------------
// When creating a 'bdlma::ConcurrentPool', clients must specify the specific
// block size managed and dispensed by the pool.  Furthermore, clients can
// optionally configure:
//
//: 1 GROWTH STRATEGY -- geometrically growing chunk size starting from 1 (in
//:   terms of the number of memory blocks per chunk), or fixed chunk size.  If
//:   the growth strategy is not specified, geometric growth is used.
//: 2 MAX BLOCKS PER CHUNK -- the maximum number of memory blocks within a
//:   chunk.  If the maximum blocks per chunk is not specified, an
//:   implementation-defined default value is used.
//: 3 BASIC ALLOCATOR -- the allocator used to supply memory to replenish the
//:   internal pool.  If not specified, the currently installed default
//:   allocator (see 'bslma_default') is used.
//
// For example, if geometric growth is used and the maximum blocks per chunk is
// specified as 30, the chunk size grows geometrically, starting from 1, until
// the specified maximum blocks per chunk, as follows:
//..
//  1, 2, 4, 8, 16, 30, 30, 30 ...
//..
// If constant growth is used, the chunk size is always the specified maximum
// blocks per chunk (or an implementation-defined value if the maximum blocks
// per chunk is not specified), for example:
//..
//  30, 30, 30 ...
//..
// A default-constructed pool has an initial chunk size of 1 (i.e., the number
// of memory blocks of a given size allocated at once to replenish a pool's
// memory), and the 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::ConcurrentPool' was created.
//
///Overloaded Global Operator 'new'
///--------------------------------
// This component overloads the global 'operator new' to allow convenient
// syntax for the construction of objects using a 'bdlma::ConcurrentPool'.  The
// 'new' operator supplied in this component takes a 'bdlma::ConcurrentPool'
// argument indicating the source of the memory.  Consider the following use of
// standard placement 'new' syntax (supplied by 'bsl_new.h') along with a
// 'bdlma::ConcurrentPool' to allocate an object of type 'T'.  Note that the
// size of 'T' must be the same or smaller than the 'blockSize' with which the
// pool is constructed:
//..
//  void f(bdlma::ConcurrentPool *pool)
//  {
//      assert(pool->blockSize() >= sizeof(T));
//
//      T *t = new (pool->allocate()) T(...);
//
//      // ...
//  }
//..
// This usage style is not exception-safe.  If the constructor of 'T' throws an
// exception, 'pool->deallocate' is never called.
//
// Supplying an overloaded global 'operator new':
//..
//  ::operator new(bsl::size_t size, bdlma::ConcurrentPool& pool);
//..
// allows for the following cleaner usage, which does not require the size
// calculation and guarantees that 'pool->deallocate' *is* called in case of an
// exception:
//..
//  void f(bdlma::ConcurrentPool *pool)
//  {
//      assert(pool->blockSize() >= sizeof(T));
//
//      T *t = new (*pool) T(...);
//
//      // ...
//..
// Also note that the analogous version of operator 'delete' should *not* be
// called directly.  Instead, this component provides a static template member
// function 'deleteObject', parameterized on 'TYPE':
//..
//      pool->deleteObject(t);
//  }
//..
// The above 'deleteObject' call is equivalent to performing the following:
//..
//  t->~TYPE();
//  pool->deallocate(t);
//..
// An overloaded operator 'delete' is supplied solely to allow the compiler to
// arrange for it to be called in case of an exception.
//
///Usage
///-----
// A 'bdlma::ConcurrentPool' can be used by node-based containers (such as
// lists, trees, and hash tables that hold multiple elements of uniform size)
// for efficient memory allocation of new elements.  The following container
// class, 'my_PooledArray', stores templatized values "out-of-place" as nodes
// in a 'vector' of pointers.  Since the size of each node is fixed and known
// *a priori*, the class uses a 'bdlma::ConcurrentPool' to allocate memory for
// the nodes to improve memory allocation efficiency:
//..
//  // my_poolarray.h
//
//  template <class T>
//  class my_PooledArray {
//      // This class implements a container that stores 'double' values
//      // out-of-place.
//
//      // DATA
//      bsl::vector<T *>      d_array_p;  // array of pooled elements
//      bdlma::ConcurrentPool d_pool;     // memory manager for array elements
//
//    private:
//      // Not implemented:
//      my_PooledArray(const my_PooledArray&);
//
//    public:
//      // CREATORS
//      explicit my_PooledArray(bslma::Allocator *basicAllocator = 0);
//          // Create a pooled array that stores the parameterized values
//          // "out-of-place".  Optionally specify a 'basicAllocator' used to
//          // supply memory.  If 'basicAllocator' is 0, the currently
//          // installed default allocator is used.
//
//      ~my_PooledArray();
//          // Destroy this array and all elements held by it.
//
//      // MANIPULATORS
//      void append(const T &value);
//          // Append the specified 'value' to this array.
//
//      void removeAll();
//          // Remove all elements from this array.
//
//      // ACCESSORS
//      int length() const;
//          // Return the number of elements in this array.
//
//      const T& operator[](int index) const;
//          // Return a reference to the non-modifiable value at the specified
//          // 'index' in this array.  The behavior is undefined unless
//          // '0 <= index < length()'.
//  };
//..
// In the 'removeAll' method, all elements are deallocated by invoking the
// pool's 'release' method.  This technique implies significant performance
// gain when the array contains many elements:
//..
//  // MANIPULATORS
//  template <class T>
//  inline
//  void my_PooledArray<T>::removeAll()
//  {
//      d_array_p.clear();
//      d_pool.release();
//  }
//
//  // ACCESSORS
//  template <class T>
//  inline
//  int my_PooledArray<T>::length() const
//  {
//      return static_cast<int>(d_array_p.size());
//  }
//
//  template <class T>
//  inline
//  const T& my_PooledArray<T>::operator[](int index) const
//  {
//      assert(0 <= index);
//      assert(index < length());
//
//      return *d_array_p[index];
//  }
//..
// Note that the growth strategy and maximum chunk size of the pool is left as
// the default value:
//..
//  // my_poolarray.cpp
//
//  // CREATORS
//  template <class T>
//  my_PooledArray<T>::my_PooledArray(bslma::Allocator *basicAllocator)
//  : d_array_p(basicAllocator)
//  , d_pool(sizeof(T), basicAllocator)
//  {
//  }
//..
// Since all memory is managed by 'd_pool', we do not have to explicitly invoke
// 'deleteObject' to reclaim outstanding memory.  The destructor of the pool
// will automatically deallocate all array elements:
//..
//  template <class T>
//  my_PooledArray<T>::~my_PooledArray()
//  {
//      // Elements are automatically deallocated when 'd_pool' is destroyed.
//  }
//..
// Note that the overloaded "placement" 'new' is used to allocate new nodes:
//..
//  template <class T>
//  void my_PooledArray<T>::append(const T& value)
//  {
//      T *tmp = new (d_pool) T(value);
//      d_array_p.push_back(tmp);
//  }
//..

#include <bdlscm_version.h>

#include <bslmt_mutex.h>

#include <bdlma_infrequentdeleteblocklist.h>

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

#include <bsls_alignmentutil.h>
#include <bsls_assert.h>
#include <bsls_atomic.h>
#include <bsls_atomicoperations.h>
#include <bsls_blockgrowth.h>
#include <bsls_platform.h>
#include <bsls_types.h>

#include <bsl_cstddef.h>

namespace BloombergLP {
namespace bdlma {

                           // ====================
                           // class ConcurrentPool
                           // ====================

class ConcurrentPool {
    // This class implements a memory pool that allocates and manages memory
    // blocks of some uniform size specified at construction.  This memory pool
    // 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.

    // PRIVATE TYPES
    struct Link {
        // This 'struct' implements a link data structure that stores the
        // address of the next link, and is used to implement the internal
        // linked list of free memory blocks.  Note that this type is
        // replicated in 'bdlma_concurrentpool.cpp' to provide access to a
        // compatible type from static methods defined in 'bdema_pool.cpp'.

        union {
            bsls::AtomicOperations::AtomicTypes::Int d_refCount;
            bsls::AlignmentUtil::MaxAlignedType      d_dummy;
        };
        Link  *volatile d_next_p;   // pointer to next link
    };

    // DATA
    bsls::Types::size_type d_blockSize;  // size of each allocated memory block
                                         // returned to client

    bsls::Types::size_type d_internalBlockSize;
                                         // actual size of each block
                                         // maintained on free list (contains
                                         // overhead for 'Link')

    int                    d_chunkSize;  // current chunk size (in
                                         // blocks-per-chunk)

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

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

    bsls::AtomicPointer<Link> d_freeList;
                                         // linked list of free memory blocks

    bdlma::InfrequentDeleteBlockList d_blockList;
                                         // memory manager for allocated memory

    bslmt::Mutex      d_mutex;           // protects access to the block list

    // PRIVATE MANIPULATORS
    void replenish();
        // Dynamically allocate a new chunk using the pool's underlying growth
        // strategy, and use the chunk to replenish the free memory list of
        // this pool.  The behavior is undefined unless the calling thread has
        // a lock on 'd_mutex'.

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

  public:
    // CREATORS
    explicit ConcurrentPool(bsls::Types::size_type  blockSize,
                            bslma::Allocator       *basicAllocator = 0);
    ConcurrentPool(bsls::Types::size_type       blockSize,
                   bsls::BlockGrowth::Strategy  growthStrategy,
                   bslma::Allocator            *basicAllocator = 0);
    ConcurrentPool(bsls::Types::size_type       blockSize,
                   bsls::BlockGrowth::Strategy  growthStrategy,
                   int                          maxBlocksPerChunk,
                   bslma::Allocator            *basicAllocator = 0);
        // Create a memory pool 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.  Optionally specify 'maxBlocksPerChunk' as the maximum
        // chunk size.  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'.  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.  The behavior is undefined unless '1 <= blockSize' and
        // '1 <= maxBlocksPerChunk'.

    ~ConcurrentPool();
        // Destroy this pool, releasing all associated memory back to the
        // underlying allocator.

    // MANIPULATORS
    void *allocate();
        // Return the address of a contiguous block of memory having the fixed
        // block size specified at construction.

    void deallocate(void *address);
        // Relinquish the memory block at the specified 'address' back to this
        // pool object for reuse.  The behavior is undefined unless 'address'
        // is non-zero, was allocated by this pool, 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 pool 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 pool 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 pool 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 pool), was allocated using this
        // pool, and has not already been deallocated.

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

    void reserveCapacity(int numBlocks);
        // Reserve memory from this pool to satisfy memory requests for at
        // least the specified 'numBlocks' before the pool replenishes.  The
        // behavior is undefined unless '0 <= numBlocks'.

    // ACCESSORS
    bsls::Types::size_type blockSize() const;
        // Return the size (in bytes) of the memory blocks allocated from this
        // pool object.  Note that all blocks dispensed by this pool have the
        // same size.

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

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

// Note that the 'new' and 'delete' operators are declared outside the
// 'BloombergLP' namespace so that they do not hide the standard placement
// 'new' and 'delete' operators (i.e.,
// 'void *operator new(bsl::size_t, void *)' and
// 'void operator delete(void *)').
//
// Also note that only the scalar versions of operators 'new' and 'delete' are
// provided, because overloading 'new' (and 'delete') with their array versions
// would cause dangerous ambiguity.  Consider what would have happened had we
// overloaded the array version of 'operator new':
//..
//  void *operator new[](bsl::size_t size, BloombergLP::bdlma::Pool& pool);
//..
// A user of 'bdlma::Pool' may expect to be able to use array 'operator new' as
// follows:
//..
//   new (*pool) my_Type[...];
//..
// The problem is that this expression returns an array that cannot be safely
// deallocated.  On the one hand, there is no syntax in C++ to invoke an
// overloaded 'operator delete'; on the other hand, the pointer returned by
// 'operator new' cannot be passed to the 'deallocate' method directly because
// the pointer is different from the one returned by the 'allocate' method.
// The compiler offsets the value of this pointer by a header, which is used to
// maintain the number of objects in the array (so that 'operator delete' can
// destroy the right number of objects).

// FREE OPERATORS
void *operator new(bsl::size_t size, BloombergLP::bdlma::ConcurrentPool& pool);
    // Return a block of memory of the specified 'size' (in bytes) allocated
    // from the specified 'pool'.  The behavior is undefined unless 'size' is
    // the same or smaller than the 'blockSize' with which 'pool' was
    // constructed.  Note that an object may allocate additional memory

    // internally, requiring the allocator to be passed in as a constructor
    // argument:
    //..
    //  my_Type *newMyType(bdlma::ConcurrentPool *pool,
    //                     bslma::Allocator      *basicAllocator)
    //  {
    //      return new (*pool) my_Type(..., basicAllocator);
    //  }
    //..
    // Also note that the analogous version of 'operator delete' should not be
    // called directly.  Instead, this component provides a static template
    // member function, 'deleteObject', parameterized by 'TYPE':
    //..
    //  void deleteMyType(my_Type *t, bdlma::ConcurrentPool *pool)
    //  {
    //      pool->deleteObject(t);
    //  }
    //..
    // 'deleteObject' performs the following:
    //..
    //  t->~my_Type();
    //  pool->deallocate(t);
    //..

inline
void operator delete(void *address, BloombergLP::bdlma::ConcurrentPool& pool);
    // Use the specified 'pool' to deallocate the memory at the specified
    // 'address'.  The behavior is undefined unless 'address' was allocated
    // using 'pool' and has not already been deallocated.  This operator is
    // supplied solely to allow the compiler to arrange for it to be called in
    // case of an exception.  Client code should not call it; use
    // 'bdlma::ConcurrentPool::deleteObject()' instead.

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

namespace BloombergLP {
namespace bdlma {

                           // --------------------
                           // class ConcurrentPool
                           // --------------------

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

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

inline
void ConcurrentPool::release()
{
    d_mutex.lock();
    d_freeList = (Link*)0;
    d_blockList.release();
    d_mutex.unlock();
}

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

// Aspects

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

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

// FREE OPERATORS
inline
void *operator new(bsl::size_t size, BloombergLP::bdlma::ConcurrentPool& pool)
{
#if defined(BSLS_ASSERT_SAFE_IS_USED)
    // gcc-4.8.1 introduced a new warning for unused typedefs, so this typedef
    // should only be present in SAFE mode builds (where it is used).

    typedef BloombergLP::bsls::AlignmentUtil Util;

    BSLS_ASSERT_SAFE(size <= pool.blockSize()
                  && Util::calculateAlignmentFromSize(size)
                       <= Util::calculateAlignmentFromSize(pool.blockSize()));
#endif

    static_cast<void>(size);  // suppress "unused parameter" warnings
    return pool.allocate();
}

inline
void operator delete(void *address, BloombergLP::bdlma::ConcurrentPool& pool)
{
    pool.deallocate(address);
}

#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 ----------------------------------