// bdlcc_sharedobjectpool.h                                           -*-C++-*-
#ifndef INCLUDED_BDLCC_SHAREDOBJECTPOOL
#define INCLUDED_BDLCC_SHAREDOBJECTPOOL

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

//@PURPOSE: Provide a thread-safe pool of shared objects.
//
//@CLASSES:
//  bdlcc::SharedObjectPool: thread-enabled container of shared objects
//
//@SEE_ALSO: bslstl_sharedptr
//
//@DESCRIPTION: This component provides a generic thread-safe pool of shared
// objects, 'bdlcc::SharedObjectPool', using the acquire-release idiom.  The
// functionality provided is identical to 'bdlcc::ObjectPool', except that
// 'getObject' returns efficiently-constructed 'bsl::shared_ptr' objects
// instead of raw pointers.  For client code that needs to provide shared
// access to objects in the pool, this functionality saves an additional
// allocation for the shared pointer itself.  Since the shared pointer and the
// object are contiguous in memory, this component also tends to improve
// performance by reducing "cache misses."
//
///Object Construction and Destruction
///-----------------------------------
// The object pool owns the memory required to store the pooled objects and the
// shared-pointer representations, and manages the construction, resetting, and
// destruction of objects.  The user may supply functors to create objects and
// to reset them to a valid state for their return to the pool; alternatively,
// this component supplies reasonable defaults.  Upon destruction the object
// pool deallocates all memory associated with the objects in the pool.  The
// behavior is undefined if there are any outstanding shared pointer references
// to the objects in the pool when it is destroyed.
//
///Creator and Resetter Template Contract
///--------------------------------------
// 'bdlcc::SharedObjectPool' is templated on two types 'CREATOR' and 'RESETTER'
// in addition to the underlying object 'TYPE'.  Objects of these types may be
// provided at construction (or defaults may be used).  The creator will be
// invoked as: 'void(*)(void*, bslma::Allocator*)'.  The resetter will be
// invoked as: 'void(*)(TYPE*)'.  The creator functor will be called to
// construct a new object of the parameterized 'TYPE' when the pool must be
// expanded (and thus it will typically invoke placement new and pass its
// allocator argument to the constructor of 'TYPE').  The resetter functor will
// be called before each object is returned to the pool, and is required to put
// the object into a state such that it is ready to be reused.  The defaults
// for these types are as follows:
//..
//    CREATOR = bdlcc::ObjectPoolFunctors::DefaultCreator
//    RESETTER = bdlcc::ObjectPoolFunctors::Nil<TYPE>
//..
// 'bdlcc::ObjectPoolFunctors::Nil' is a no-op; it is only suitable if the
// objects stored in the pool are *always* in a valid state to be reused.
// Otherwise - that is, if anything must be done to render the objects ready
// for reuse - another kind of 'RESETTER' should be provided (so long as that
// type supplies 'void(*)(TYPE*)').  In 'bdlcc::ObjectPoolFunctors', the
// classes 'Clear', 'RemoveAll', and 'Reset' are all acceptable types for
// 'RESETTER'.  Since these "functor" types are fully inline, it is generally
// most efficient to define 'reset()' (or 'clear()' or 'removeAll()') in the
// underlying 'TYPE' and allow the functor to call that method.  The 'CREATOR'
// functor defaults to an object that invokes the default constructor with
// placement new, passing the allocator argument if the type traits of the
// object indicate it uses an allocator (see 'bslma_usesbslmaallocator').  If a
// custom creator functor or a custom 'CREATOR' type is specified, it is the
// user's responsibility to ensure that it correctly passes its allocator
// argument through to the constructor of 'TYPE' if 'TYPE' uses allocator.
//
///Exception Safety
///----------------
// There are two potential sources of exceptions in this component: memory
// allocation and object construction.  The object pool is exception-neutral
// with full guarantee of rollback for the following methods: if an exception
// is thrown in 'getObject', 'reserveCapacity', or 'increaseCapacity', then the
// pool is in a valid unmodified state (i.e., identical to prior the call to
// 'getObject').  No other method of 'bdlcc::SharedObjectPool' can throw.
//
///Pool Replenishment Policy
///-------------------------
// The 'growBy' parameter can be specified in the pool's constructor to
// instruct the pool how it is to increase its capacity each time the pool is
// depleted.  If 'growBy' is positive, the pool always replenishes itself with
// enough objects so that it can satisfy at least 'growBy' object requests
// before the next replenishment.  If 'growBy' is negative, the pool will
// increase its capacity geometrically until it exceeds the internal maximum
// (which itself is implementation-defined), and after that it will be
// replenished with constant number of objects.  If 'growBy' is not specified,
// an implementation-defined default will be chosen.  The behavior is undefined
// if growBy is 0.
//
///Usage
///-----
// This component is intended to improve the efficiency of code which provides
// shared pointers to pooled objects.  As an example, consider a class which
// maintains a pool of 'vector<char>' objects and provides shared pointers to
// them.  Using 'bdlcc::ObjectPool', the class might be implemented like this:
//..
//  typedef vector<char> CharArray;
//
//  class SlowCharArrayPool {
//      bdlma::ConcurrentPoolAllocator d_spAllocator;  // alloc. shared pointer
//      bdlcc::ObjectPool<CharArray>   d_charArrayPool;  // supply charArrays
//
//      static void createCharArray(void *address, bslma::Allocator *allocator)
//      {
//          new (address) CharArray(allocator);
//      }
//
//      static void resetAndReturnCharArray(
//                                     CharArray                    *charArray,
//                                     bdlcc::ObjectPool<CharArray> *pool)
//      {
//          charArray->clear();
//          pool->releaseObject(charArray);
//      }
//
//    private:
//      // Not implemented:
//      SlowCharArrayPool(const SlowCharArrayPool&);
//
//    public:
//      SlowCharArrayPool(bslma::Allocator *basicAllocator = 0)
//      : d_spAllocator(basicAllocator)
//      , d_charArrayPool(bdlf::BindUtil::bind(
//                                         &SlowCharArrayPool::createCharArray,
//                                         bdlf::PlaceHolders::_1,
//                                         basicAllocator),
//                        -1,
//                        basicAllocator)
//      {
//      }
//
//      void getCharArray(bsl::shared_ptr<CharArray> *charArray_sp)
//      {
//          charArray_sp->reset(d_charArrayPool.getObject(),
//                              bdlf::BindUtil::bind(
//                                 &SlowCharArrayPool::resetAndReturnCharArray,
//                                 bdlf::PlaceHolders::_1,
//                                 &d_charArrayPool),
//                              &d_spAllocator);
//      }
//  };
//..
// Note that 'SlowCharArrayPool' must allocate the shared pointer itself from
// its 'd_spAllocator' in addition to allocating the charArray from its pool.
// Moreover, note that since the same function will handle resetting the object
// and returning it to the pool, we must define a special function for that
// purpose and bind its arguments.
//
// We can solve both of these issues by using 'bdlcc::SharedObjectPool'
// instead:
//..
//  class FastCharArrayPool {
//      typedef bdlcc::SharedObjectPool<
//              CharArray,
//              bdlcc::ObjectPoolFunctors::DefaultCreator,
//              bdlcc::ObjectPoolFunctors::Clear<CharArray> > CharArrayPool;
//
//      CharArrayPool d_charArrayPool;     // supply charArrays
//
//      static void createCharArray(void *address, bslma::Allocator *allocator)
//      {
//          new (address) CharArray(allocator);
//      }
//
//    private:
//      // Not implemented:
//      FastCharArrayPool(const FastCharArrayPool&);
//
//    public:
//      FastCharArrayPool(bslma::Allocator *basicAllocator = 0)
//      : d_charArrayPool(bdlf::BindUtil::bind(
//                                         &FastCharArrayPool::createCharArray,
//                                         bdlf::PlaceHolders::_1,
//                                         bdlf::PlaceHolders::_2),
//                        -1,
//                        basicAllocator)
//      {
//      }
//
//      void getCharArray(bsl::shared_ptr<CharArray> *charArray_sp)
//      {
//          *charArray_sp = d_charArrayPool.getObject();
//      }
//  };
//..
// Now the shared pointer and the object are allocated as one unit from the
// same allocator.  In addition, the resetter method is a fully-inlined class
// that is only responsible for resetting the object, improving efficiency and
// simplifying the design.  We can verify that use of 'bdlcc::SharedObjectPool'
// reduces the number of allocation requests:
//..
//  bslma::TestAllocator slowAllocator, fastAllocator;
//  {
//      SlowCharArrayPool slowPool(&slowAllocator);
//      FastCharArrayPool fastPool(&fastAllocator);
//
//      bsl::shared_ptr<CharArray> charArray_sp;
//
//      fastPool.getCharArray(&charArray_sp);
//      slowPool.getCharArray(&charArray_sp);  // throw away the first array
//  }
//
//  assert(2 == slowAllocator.numAllocations());
//  assert(1 == fastAllocator.numAllocations());
//  assert(0 == slowAllocator.numBytesInUse());
//  assert(0 == fastAllocator.numBytesInUse());
//..

#include <bdlscm_version.h>

#include <bdlcc_objectpool.h>

#include <bdlf_bind.h>
#include <bdlf_placeholder.h>

#include <bslalg_constructorproxy.h>
#include <bslalg_scalarprimitives.h>

#include <bslma_allocator.h>
#include <bslma_sharedptrrep.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_objectbuffer.h>
#include <bsls_platform.h>

#include <bsl_memory.h>

namespace BloombergLP {
namespace bdlcc {

                         // ==========================
                         // class SharedObjectPool_Rep
                         // ==========================

template <class TYPE, class RESETTER>
class SharedObjectPool_Rep: public bslma::SharedPtrRep {

    typedef SharedObjectPool_Rep<TYPE, RESETTER> MyType;
    typedef ObjectPool<MyType,
                            ObjectPoolFunctors::DefaultCreator,
                            ObjectPoolFunctors::Reset<MyType> >
                                                      PoolType;

    // DATA
    bslalg::ConstructorProxy<RESETTER> d_objectResetter;

    PoolType                          *d_pool_p;   // object pool (held)
    bsls::ObjectBuffer<TYPE>           d_instance; // area for embedded
                                                   // instance

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

  public:
    // CREATORS
    template <class CREATOR>
    SharedObjectPool_Rep(
                    CREATOR*                                   objectCreator,
                    const bslalg::ConstructorProxy<RESETTER>&  objectResetter,
                    PoolType                                  *pool,
                    bslma::Allocator                          *basicAllocator);
        // Construct a new rep object that, upon release, will invoke the
        // specified 'objectResetter' and return itself to the specified
        // 'pool'; then invoke 'objectCreator' to construct an object of 'TYPE'
        // embedded within the new rep object.  Use the specified
        // 'basicAllocator' to supply memory.  If 'basicAllocator' is 0, the
        // currently installed default allocator is used.

    ~SharedObjectPool_Rep();
        // Destroy this representation object and the embedded instance of
        // 'TYPE'.

    // MANIPULATORS
    virtual void disposeRep();
        // Release this representation object.  This method is invoked when the
        // number of weak references and the number of strong references reach
        // zero.  This virtual override will return the object, and this
        // representation, to the associated pool.

    virtual void disposeObject();
        // Release the object being managed by this representation.  This
        // method is invoked when the number of strong references reaches zero.
        // Note that if there are any weak references to the shared object then
        // this function does nothing, including not destroying the object or
        // returning it to the pool.

    void reset();
        // Invoke the object resetter specified at construction on the
        // associated object.

    virtual void *getDeleter(const std::type_info& type);
        // Return NULL.  Shared object pools strictly control the delete policy
        // for their objects, and do not expose it to end users.

    // ACCESSORS
    virtual void *originalPtr() const;
        // Return (untyped) address of the object managed by this
        // representation.  This virtual override effectively returns
        // "(void*)ptr()".

    TYPE *ptr();
        // Return a pointer to the in-place object.
};

                           // ======================
                           // class SharedObjectPool
                           // ======================

template <class TYPE,
          class CREATOR=ObjectPoolFunctors::DefaultCreator,
          class RESETTER=ObjectPoolFunctors::Nil<TYPE> >
class SharedObjectPool {

    typedef SharedObjectPool<TYPE, CREATOR, RESETTER> MyType;
    typedef SharedObjectPool_Rep<TYPE, RESETTER>      RepType;
    typedef ObjectPool<RepType,
                            ObjectPoolFunctors::DefaultCreator,
                            ObjectPoolFunctors::Reset<RepType> >
                                                           PoolType;

    typename ObjectPool_ProxyPicker<CREATOR>::template Selector<TYPE>::Proxy
                                d_objectCreator; // functor for object creation

    bslalg::ConstructorProxy<RESETTER>
                                d_objectResetter;  // functor to reset object

    PoolType                    d_pool;           // object pool (owned)

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

    void constructRepObject(void *memory, bslma::Allocator *alloc);
        // Initializes a newly constructed SharedObjectPool_Rep object

  public:
    // TYPES
    typedef CREATOR    CreatorType;
    typedef RESETTER   ResetterType;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(SharedObjectPool,
                                   bslma::UsesBslmaAllocator);

    // CREATORS
    explicit
    SharedObjectPool(int growBy = -1, bslma::Allocator *basicAllocator = 0);

    explicit
    SharedObjectPool(const CREATOR&    objectCreator,
                     bslma::Allocator *basicAllocator = 0);

    SharedObjectPool(const CREATOR&    objectCreator,
                     int               growBy,
                     bslma::Allocator *basicAllocator = 0);

    SharedObjectPool(const CREATOR&    objectCreator,
                     const RESETTER&   objectResetter,
                     int               growBy = -1,
                     bslma::Allocator *basicAllocator = 0);

        // Create an object pool that dispenses shared pointers to TYPE.  When
        // the pool is depleted, it increases its capacity according to the
        // optionally specified 'growBy' value.  If 'growBy' is positive, the
        // pool always increases by at least 'growBy'.  If 'growBy' is
        // negative, the amount of increase begins at '-growBy' and then grows
        // geometrically up to an implementation-defined maximum.  The
        // optionally specified 'objectCreator' is called whenever objects must
        // be constructed.  If 'objectCreator' is not specified and the
        // parameterized 'CREATOR' is the default type (that is,
        // 'ObjectPoolFunctors::DefaultCreator'), a function that calls the
        // default constructor of 'TYPE' with placement new, passing this
        // pool's allocator if TYPE uses allocator, is used.  If the
        // parameterized 'CREATOR' is some other type, and 'objectCreator' is
        // not specified, the default value of the 'CREATOR' type is used.  The
        // optionally specified 'objectResetter' is invoked with a pointer to
        // an object of 'TYPE' when the object is returned to the pool.  It
        // must reset the object into a valid state for reuse.  If
        // 'objectResetter' is not specified, a default RESETTER object is
        // used.  Optionally specify a basic allocator to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined if 'growBy' is 0.

    ~SharedObjectPool();
        // Destroy this object pool.  All objects created by this pool are
        // destroyed (even if some of them are still in use) and memory is
        // reclaimed.

    // MANIPULATORS
    bsl::shared_ptr<TYPE> getObject();
        // Return a pointer to an object from this object pool.  When the last
        // shared pointer to the object is destroyed, the object will be reset
        // as specified at construction and then returned to the pool.  If this
        // pool is empty, it is replenished according to the strategy specified
        // at construction.

    void increaseCapacity(int growBy);
        // Create the specified 'growBy' objects and add them to this object
        // pool.  The behavior is undefined unless '0 <= growBy'.

    void reserveCapacity(int growBy);
        // Create enough objects to satisfy requests for at least the specified
        // 'growBy' objects before the next replenishment.  The behavior is
        // undefined unless '0 <= growBy'.  Note that this method is different
        // from 'increaseCapacity' in that the number of created objects may be
        // less than 'growBy'.

    // ACCESSORS
    int numAvailableObjects() const;
        // Return a *snapshot* of the number of objects available in this pool.

    int numObjects() const;
        // Return the (instantaneous) number of objects managed by this pool.
        // This includes both the objects available in the pool and the objects
        // that were allocated from the pool and not yet released.
};

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

                         // --------------------------
                         // class SharedObjectPool_Rep
                         // --------------------------

// CREATORS
template <class TYPE, class RESETTER>
template <class CREATOR>
inline
SharedObjectPool_Rep<TYPE, RESETTER>::SharedObjectPool_Rep(
                     CREATOR*                                   objectCreator,
                     const bslalg::ConstructorProxy<RESETTER>&  objectResetter,
                     PoolType                                  *pool,
                     bslma::Allocator                          *basicAllocator)
: d_objectResetter(objectResetter,basicAllocator)
, d_pool_p(pool)
{
    (*objectCreator)(d_instance.buffer(), basicAllocator);
}

template <class TYPE, class RESETTER>
inline
SharedObjectPool_Rep<TYPE, RESETTER>::~SharedObjectPool_Rep()
{
    d_instance.object().~TYPE();
}

// MANIPULATORS
template <class TYPE, class RESETTER>
inline
void SharedObjectPool_Rep<TYPE, RESETTER>::reset()
{
    d_objectResetter.object()(&d_instance.object());
}

template <class TYPE, class RESETTER>
inline
void SharedObjectPool_Rep<TYPE, RESETTER>::disposeRep()
{
    d_pool_p->releaseObject(this);
}

template <class TYPE, class RESETTER>
inline
void *SharedObjectPool_Rep<TYPE, RESETTER>::getDeleter(
                                                const std::type_info& /*type*/)
{
    return 0;
}

template <class TYPE, class RESETTER>
inline
void SharedObjectPool_Rep<TYPE, RESETTER>::disposeObject()
{
    // No-op
}

// ACCESSORS
template <class TYPE, class RESETTER>
inline
void *SharedObjectPool_Rep<TYPE, RESETTER>::originalPtr() const
{
    return const_cast<void*>(static_cast<const void*>(d_instance.buffer()));
}

template <class TYPE, class RESETTER>
inline
TYPE *SharedObjectPool_Rep<TYPE, RESETTER>::ptr()
{
    return &d_instance.object();
}

                             // ----------------
                             // SharedObjectPool
                             // ----------------

// PRIVATE
template <class TYPE, class CREATOR, class RESETTER>
inline
void SharedObjectPool<TYPE, CREATOR, RESETTER>::constructRepObject(
                                                      void             *memory,
                                                      bslma::Allocator *alloc)
{
    RepType *r = new (memory) RepType(&d_objectCreator.object(),
                                      d_objectResetter,
                                      &d_pool,
                                      alloc);
    r->resetCountsRaw(0, 0);
}

}  // close package namespace

// CREATORS
#if defined(BSLS_PLATFORM_CMP_MSVC)
// Visual C++ complains about any use of the 'this' pointer in a member
// initializer of a constructor.  The use cases below seem perfectly safe and
// correct, but there is no way to eliminate this warning from a prominent
// header file other than disabling it via a pragma.
#pragma warning(push)
#pragma warning(disable : 4355) // used 'this' in member initializer
#endif

namespace bdlcc {

template <class TYPE, class CREATOR, class RESETTER>
inline
SharedObjectPool<TYPE, CREATOR, RESETTER>::SharedObjectPool(
                                              const CREATOR&    objectCreator,
                                              const RESETTER&   objectResetter,
                                              int               growBy,
                                              bslma::Allocator *basicAllocator)
: d_objectCreator(objectCreator,basicAllocator)
, d_objectResetter(objectResetter,basicAllocator)
, d_pool(bdlf::BindUtil::bind(&MyType::constructRepObject, this,
                             bdlf::PlaceHolders::_1,
                             bdlf::PlaceHolders::_2),
         growBy, basicAllocator)
{
}

template <class TYPE, class CREATOR, class RESETTER>
inline
SharedObjectPool<TYPE, CREATOR, RESETTER>::SharedObjectPool(
                                              int               growBy,
                                              bslma::Allocator *basicAllocator)
: d_objectCreator(basicAllocator)
, d_objectResetter(basicAllocator)
, d_pool(bdlf::BindUtil::bind(&MyType::constructRepObject, this,
                             bdlf::PlaceHolders::_1,
                             bdlf::PlaceHolders::_2),
         growBy, basicAllocator)
{
}

template <class TYPE, class CREATOR, class RESETTER>
inline
SharedObjectPool<TYPE, CREATOR, RESETTER>::SharedObjectPool(
                                              const CREATOR&    objectCreator,
                                              int               growBy,
                                              bslma::Allocator *basicAllocator)
: d_objectCreator(objectCreator, basicAllocator)
, d_objectResetter(basicAllocator)
, d_pool(bdlf::BindUtil::bind(&MyType::constructRepObject, this,
                             bdlf::PlaceHolders::_1,
                             bdlf::PlaceHolders::_2),
         growBy, basicAllocator)
{
}

template <class TYPE, class CREATOR, class RESETTER>
inline
SharedObjectPool<TYPE, CREATOR, RESETTER>::SharedObjectPool(
                                              const CREATOR&    objectCreator,
                                              bslma::Allocator *basicAllocator)
: d_objectCreator(objectCreator, basicAllocator)
, d_objectResetter(basicAllocator)
, d_pool(bdlf::BindUtil::bind(&MyType::constructRepObject, this,
                             bdlf::PlaceHolders::_1,
                             bdlf::PlaceHolders::_2),
         -1, basicAllocator)
{
}
}  // close package namespace

#if defined(BSLS_PLATFORM_CMP_MSVC)
// Restore warnings so as not to affect their state in other files.
#pragma warning(pop)
#endif

namespace bdlcc {

template <class TYPE, class CREATOR, class RESETTER>
inline
SharedObjectPool<TYPE, CREATOR, RESETTER>::~SharedObjectPool()
{
}

// MANIPULATORS
template <class TYPE, class CREATOR, class RESETTER>
inline
bsl::shared_ptr<TYPE>
SharedObjectPool<TYPE, CREATOR, RESETTER>::getObject()
{
    RepType *rep = d_pool.getObject();
    bslma::SharedPtrRep *genericRep = rep;
    genericRep->resetCountsRaw(1, 0);

    return bsl::shared_ptr<TYPE>(rep->ptr(), genericRep);
}

template <class TYPE, class CREATOR, class RESETTER>
inline
void
SharedObjectPool<TYPE, CREATOR, RESETTER>::increaseCapacity(int growBy)
{
    d_pool.increaseCapacity(growBy);
}

template <class TYPE, class CREATOR, class RESETTER>
inline
void
SharedObjectPool<TYPE, CREATOR, RESETTER>::reserveCapacity(int growBy)
{
    d_pool.reserveCapacity(growBy);
}

// ACCESSORS
template <class TYPE, class CREATOR, class RESETTER>
inline
int SharedObjectPool<TYPE, CREATOR, RESETTER>::numAvailableObjects() const
{
    return d_pool.numAvailableObjects();
}

template <class TYPE, class CREATOR, class RESETTER>
inline
int SharedObjectPool<TYPE, CREATOR, RESETTER>::numObjects() const
{
    return d_pool.numObjects();
}
}  // 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 ----------------------------------