BDE 4.14.0 Production release
Loading...
Searching...
No Matches

Functions

void * operator new (bsl::size_t size, BloombergLP::bdlma::Pool &pool)
 
void operator delete (void *address, BloombergLP::bdlma::Pool &pool)
 

Detailed Description

Outline

Purpose

Provide efficient allocation of memory blocks of uniform size.

Classes

Description

This component implements a memory pool, bdlma::Pool, that allocates and manages maximally-aligned memory blocks of some uniform size specified at construction. A bdlma::Pool 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::Pool 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::Pool, 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 is used (see bslma_default ).

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::Pool 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::Pool. The new operator supplied in this component takes a bdlma::Pool 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::Pool 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::Pool *pool)
{
assert(pool->blockSize() >= sizeof(T));
T *t = new (pool->allocate()) T(...);
// ...
}
Definition bdlma_pool.h:335
bsls::Types::size_type blockSize() const
Definition bdlma_pool.h:613
void * allocate()
Definition bdlma_pool.h:562

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, BloombergLP::bdlma::Pool& pool);

allows for the following cleaner usage, which does not require the size calculation and guarantees that pool->deallocate is called in the case of an exception:

void f(bdlma::Pool *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);
}
void deleteObject(const TYPE *object)
Definition bdlma_pool.h:590

The above deleteObject call is equivalent to performing the following:

t->~TYPE();
pool->deallocate(t);
void deallocate(void *address)
Definition bdlma_pool.h:580

An overloaded operator delete is supplied solely to allow the compiler to arrange for it to be called in case of an exception.

Usage

This section illustrates intended use of this component.

Example 1: Using a bdlma::Pool for Efficient Memory Allocation

A bdlma::Pool 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 template class, my_PooledArray, stores values of (template parameter) TYPE "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::Pool to allocate memory for the nodes to improve memory allocation efficiency. Note that for simplicity, we assume that TYPE does not require an allocator, and that calls to the destructor of TYPE can be elided.

First, we define the interface of our my_PooledArray template class:

// my_poolarray.h
template <class TYPE>
class my_PooledArray {
// This class implements a container that stores values of (template
// parameter) 'TYPE' out-of-place. It is assumed that 'TYPE' does not
// require an allocator, and that calls to the destructor of 'TYPE' can
// be elided.
// DATA
bsl::vector<TYPE *> d_array_p; // array of pooled elements
bdlma::Pool 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 'TYPE' element 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 TYPE& value);
// Append the specified 'value' to this array.
void removeAll();
// Remove all elements from this array.
// ACCESSORS
bsl::size_t length() const;
// Return the number of elements in this array.
const TYPE& operator[](int index) const;
// Return a reference providing non-modifiable access to the value
// at the specified 'index' in this array. The behavior is
// undefined unless '0 <= index < length()'.
};
Definition bslstl_vector.h:1025
Definition bslma_allocator.h:457

Next, we provide the implementation of the my_PooledArray methods that are defined inline.

Note that in the removeAll method, all elements are deallocated by simply invoking the pool's release method. This technique implies significant performance gain when the array contains many elements:

// MANIPULATORS
template <class TYPE>
inline
void my_PooledArray<TYPE>::removeAll()
{
d_array_p.clear();
d_pool.release();
}
// ACCESSORS
template <class TYPE>
inline
bsl::size_t my_PooledArray<TYPE>::length() const
{
return d_array_p.size();
}
template <class TYPE>
inline
const TYPE& my_PooledArray<TYPE>::operator[](int index) const
{
assert(0 <= index);
assert(index < static_cast<int>(length()));
return *d_array_p[index];
}

Next, we provide the implementation of the my_PooledArray methods that are defined in the .cpp file.

Note that the growth strategy and maximum chunk size of the pool defaults to those provided by bdlma::Pool:

// my_poolarray.cpp
// CREATORS
template <class TYPE>
my_PooledArray<TYPE>::my_PooledArray(bslma::Allocator *basicAllocator)
: d_array_p(basicAllocator)
, d_pool(sizeof(TYPE), 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 TYPE>
my_PooledArray<TYPE>::~my_PooledArray()
{
// Elements are automatically deallocated when 'd_pool' is destroyed.
}

Finally, note that the overloaded "placement" new is used to allocate new nodes in the append method:

// MANIPULATORS
template <class TYPE>
void my_PooledArray<TYPE>::append(const TYPE& value)
{
TYPE *tmp = new (d_pool) TYPE(value);
d_array_p.push_back(tmp);
}

Function Documentation

◆ operator delete()

void operator delete ( void *  address,
BloombergLP::bdlma::Pool &  pool 
)
inline

Use the specified pool to deallocate the memory at the specified address. The behavior is undefined unless address is non-zero, was allocated using pool, and has not already been deallocated. Note that this operator is supplied solely to allow the compiler to arrange for it to be called in the case of an exception.

◆ operator new()

void * operator new ( bsl::size_t  size,
BloombergLP::bdlma::Pool &  pool 
)
inline

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::Pool *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::Pool *pool)
{
pool->deleteObject(t);
}

deleteObject performs the following:

t->~my_Type();
pool->deallocate(t);