Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlcc_sharedobjectpool
[Package bdlcc]

Provide a thread-safe pool of shared objects. More...

Namespaces

namespace  bdlcc

Detailed Description

Outline
Purpose:
Provide a thread-safe pool of shared objects.
Classes:
bdlcc::SharedObjectPool thread-enabled container of shared objects
See also:
Component 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: 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());