Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Boost Pool Interfaces - What interfaces are provided and when to use each one.

Pool Interfaces
pool
Object_pool
Singleton_pool
pool_allocator
Introduction

There are several interfaces provided which allow users great flexibility in how they want to use Pools. Review the concepts document to get the basic understanding of how the various pools work.

Terminology and Tradeoffs

Object Usage vs. Singleton Usage

Object Usage is the method where each Pool is an object that may be created and destroyed. Destroying a Pool implicitly frees all chunks that have been allocated from it.

Singleton Usage is the method where each Pool is an object with static duration; that is, it will not be destroyed until program exit. Pool objects with Singleton Usage may be shared; thus, Singleton Usage implies thread-safety as well. System memory allocated by Pool objects with Singleton Usage may be freed through release_memory or purge_memory.

Out-of-Memory Conditions: Exceptions vs. Null Return

Some Pool interfaces throw exceptions when out-of-memory; others will return 0. In general, unless mandated by the Standard, Pool interfaces will always prefer to return 0 instead of throwing an exception.

Ordered versus unordered

An ordered pool maintains it's free list in order of the address of each free block - this is the most efficient way if you're likely to allocate arrays of objects. However, freeing an object can be O(N) in the number of currently free blocks which can be prohibitively expensive in some situations.

An unordered pool does not maintain it's free list in any particular order, as a result allocation and freeing single objects is very fast, but allocating arrays may be slow (and in particular the pool may not be aware that it contains enough free memory for the allocation request, and unnecessarily allocate more memory).

The pool interface is a simple Object Usage interface with Null Return.

pool is a fast memory allocator, and guarantees proper alignment of all allocated chunks.

pool.hpp provides two UserAllocator classes and a template class pool, which extends and generalizes the framework provided by the Simple Segregated Storage solution. For information on other pool-based interfaces, see the other Pool Interfaces.

Synopsis

There are two UserAllocator classes provided. Both of them are in pool.hpp.

The default value for the template parameter UserAllocator is always default_user_allocator_new_delete.

struct default_user_allocator_new_delete
{
  typedef std::size_t size_type;
  typedef std::ptrdiff_t difference_type;

  static char * malloc(const size_type bytes)
  { return new (std::nothrow) char[bytes]; }
  static void free(char * const block)
  { delete [] block; }
};

struct default_user_allocator_malloc_free
{
  typedef std::size_t size_type;
  typedef std::ptrdiff_t difference_type;

  static char * malloc(const size_type bytes)
  { return reinterpret_cast<char *>(std::malloc(bytes)); }
  static void free(char * const block)
  { std::free(block); }
};

template <typename UserAllocator = default_user_allocator_new_delete>
class pool
{
  private:
    pool(const pool &);
    void operator=(const pool &);

  public:
    typedef UserAllocator user_allocator;
    typedef typename UserAllocator::size_type size_type;
    typedef typename UserAllocator::difference_type difference_type;

    explicit pool(size_type requested_size);
    ~pool();

    bool release_memory();
    bool purge_memory();

    bool is_from(void * chunk) const;
    size_type get_requested_size() const;

    void * malloc();
    void * ordered_malloc();
    void * ordered_malloc(size_type n);

    void free(void * chunk);
    void ordered_free(void * chunk);
    void free(void * chunks, size_type n);
    void ordered_free(void * chunks, size_type n);
};

Example:

void func()
{
  boost::pool<> p(sizeof(int));
  for (int i = 0; i < 10000; ++i)
  {
    int * const t = p.malloc();
    ... // Do something with t; don't take the time to free() it.
  }
} // on function exit, p is destroyed, and all malloc()'ed ints are implicitly freed.

The template class object_pool interface is an Object Usage interface with Null Return, but is aware of the type of the object for which it is allocating chunks. On destruction, any chunks that have been allocated from that object_pool will have their destructors called.

object_pool.hpp provides a template type that can be used for fast and efficient memory allocation. It also provides automatic destruction of non-deallocated objects.

For information on other pool-based interfaces, see the other Pool Interfaces.

Synopsis

template <typename ElementType, typename UserAllocator = default_user_allocator_new_delete>
class object_pool
{
  private:
    object_pool(const object_pool &);
    void operator=(const object_pool &);

  public:
    typedef ElementType element_type;
    typedef UserAllocator user_allocator;
    typedef typename pool<UserAllocator>::size_type size_type;
    typedef typename pool<UserAllocator>::difference_type difference_type;

    object_pool();
    ~object_pool();

    element_type * malloc();
    void free(element_type * p);
    bool is_from(element_type * p) const;

    element_type * construct();
    // other construct() functions
    void destroy(element_type * p);
};

Template Parameters

ElementType

The template parameter is the type of object to allocate/deallocate. It must have a non-throwing destructor.

UserAllocator

Defines the method that the underlying Pool will use to allocate memory from the system. Default is default_user_allocator_new_delete. See __UserAllocator for details.

Example: struct X { ... }; // has destructor with side-effects.

void func()
{
  boost::object_pool<X> p;
  for (int i = 0; i < 10000; ++i)
  {
    X * const t = p.malloc();
    ... // Do something with t; don't take the time to free() it.
  }
} // on function exit, p is destroyed, and all destructors for the X objects are called.

The singleton_pool interface at singleton_pool.hpp is a Singleton Usage interface with Null Return. It's just the same as the pool interface but with Singleton Usage instead.

Synopsis

template <typename Tag, unsigned RequestedSize,
    typename UserAllocator = default_user_allocator_new_delete>
struct singleton_pool
{
  public:
    typedef Tag tag;
    typedef UserAllocator user_allocator;
    typedef typename pool<UserAllocator>::size_type size_type;
    typedef typename pool<UserAllocator>::difference_type difference_type;

    static const unsigned requested_size = RequestedSize;

  private:
    static pool<size_type> p; // exposition only!

    singleton_pool();

  public:
    static bool is_from(void * ptr);

    static void * malloc();
    static void * ordered_malloc();
    static void * ordered_malloc(size_type n);

    static void free(void * ptr);
    static void ordered_free(void * ptr);
    static void free(void * ptr, std::size_t n);
    static void ordered_free(void * ptr, size_type n);

    static bool release_memory();
    static bool purge_memory();
};

Notes

The underlying pool p referenced by the static functions in singleton_pool is actually declared in a way so that it is:

  • Thread-safe if there is only one thread running before main() begins and after main() ends. All of the static functions of singleton_pool synchronize their access to p.
  • Guaranteed to be constructed before it is used, so that the simple static object in the synopsis above would actually be an incorrect implementation. The actual implementation to guarantee this is considerably more complicated.

Note that a different underlying pool p exists for each different set of template parameters, including implementation-specific ones.

Template Parameters

Tag

The Tag template parameter allows different unbounded sets of singleton pools to exist. For example, the pool allocators use two tag classes to ensure that the two different allocator types never share the same underlying singleton pool.

Tag is never actually used by singleton_pool.

RequestedSize The requested size of memory chunks to allocate. This is passed as a constructor parameter to the underlying pool. Must be greater than 0.

UserAllocator

Defines the method that the underlying pool will use to allocate memory from the system. See User Allocators for details.

Example: struct MyPoolTag { };

typedef boost::singleton_pool<MyPoolTag, sizeof(int)> my_pool;
void func()
{
  for (int i = 0; i < 10000; ++i)
  {
    int * const t = my_pool::malloc();
    ... // Do something with t; don't take the time to free() it.
  }
  // Explicitly free all malloc()'ed ints.
  my_pool::purge_memory();
}

The pool_allocator interface is a Singleton Usage interface with Exceptions. It is built on the singleton_pool interface, and provides a Standard Allocator-compliant class (for use in containers, etc.).

Introduction

pool_alloc.hpp

Provides two template types that can be used for fast and efficient memory allocation. These types both satisfy the Standard Allocator requirements [20.1.5] and the additional requirements in [20.1.5/4], so they can be used with Standard or user-supplied containers.

For information on other pool-based interfaces, see the other Pool Interfaces.

Synopsis

struct pool_allocator_tag { };

template <typename T,
    typename UserAllocator = default_user_allocator_new_delete>
class pool_allocator
{
  public:
    typedef UserAllocator user_allocator;
    typedef T value_type;
    typedef value_type * pointer;
    typedef const value_type * const_pointer;
    typedef value_type & reference;
    typedef const value_type & const_reference;
    typedef typename pool<UserAllocator>::size_type size_type;
    typedef typename pool<UserAllcoator>::difference_type difference_type;

    template <typename U>
    struct rebind
    { typedef pool_allocator<U, UserAllocator> other; };

  public:
    pool_allocator();
    pool_allocator(const pool_allocator &);
    // The following is not explicit, mimicking std::allocator [20.4.1]
    template <typename U>
    pool_allocator(const pool_allocator<U, UserAllocator> &);
    pool_allocator & operator=(const pool_allocator &);
    ~pool_allocator();

    static pointer address(reference r);
    static const_pointer address(const_reference s);
    static size_type max_size();
    static void construct(pointer ptr, const value_type & t);
    static void destroy(pointer ptr);

    bool operator==(const pool_allocator &) const;
    bool operator!=(const pool_allocator &) const;

    static pointer allocate(size_type n);
    static pointer allocate(size_type n, pointer);
    static void deallocate(pointer ptr, size_type n);
};

struct fast_pool_allocator_tag { };

template <typename T
    typename UserAllocator = default_user_allocator_new_delete>
class fast_pool_allocator
{
  public:
    typedef UserAllocator user_allocator;
    typedef T value_type;
    typedef value_type * pointer;
    typedef const value_type * const_pointer;
    typedef value_type & reference;
    typedef const value_type & const_reference;
    typedef typename pool<UserAllocator>::size_type size_type;
    typedef typename pool<UserAllocator>::difference_type difference_type;

    template <typename U>
    struct rebind
    { typedef fast_pool_allocator<U, UserAllocator> other; };

  public:
    fast_pool_allocator();
    fast_pool_allocator(const fast_pool_allocator &);
    // The following is not explicit, mimicking std::allocator [20.4.1]
    template <typename U>
    fast_pool_allocator(const fast_pool_allocator<U, UserAllocator> &);
    fast_pool_allocator & operator=(const fast_pool_allocator &);
    ~fast_pool_allocator();

    static pointer address(reference r);
    static const_pointer address(const_reference s);
    static size_type max_size();
    static void construct(pointer ptr, const value_type & t);
    static void destroy(pointer ptr);

    bool operator==(const fast_pool_allocator &) const;
    bool operator!=(const fast_pool_allocator &) const;

    static pointer allocate(size_type n);
    static pointer allocate(size_type n, pointer);
    static void deallocate(pointer ptr, size_type n);

    static pointer allocate();
    static void deallocate(pointer ptr);
};

Template Parameters

T The first template parameter is the type of object to allocate/deallocate.

UserAllocator Defines the method that the underlying Pool will use to allocate memory from the system. See User Allocators for details.

Example:

void func()
{
  std::vector<int, boost::pool_allocator<int> > v;
  for (int i = 0; i < 10000; ++i)
    v.push_back(13);
} // Exiting the function does NOT free the system memory allocated by the pool allocator.
  // You must call
  //  boost::singleton_pool<boost::pool_allocator_tag, sizeof(int)>::release_memory();
  // in order to force freeing the system memory.

PrevUpHomeNext