A pool resource consists of a collection of
pools,
serving requests for different block sizes
. Each individual pool manages a collection of
chunks
that are in turn divided into blocks of uniform size,
returned via calls to
do_allocate. Each call to
do_allocate(size, alignment) is dispatched
to the pool serving the smallest blocks accommodating at least
size bytes
.When a particular pool is exhausted,
allocating a block from that pool results in the allocation
of an additional chunk of memory from the
upstream allocator
(supplied at construction), thus replenishing the pool
. With each successive replenishment,
the chunk size obtained increases geometrically
. [
Note 1:
By allocating memory in chunks,
the pooling strategy increases the chance that consecutive allocations
will be close together in memory
. —
end note]
Allocation requests that exceed the largest block size of any pool
are fulfilled directly from the upstream allocator
.A
pool_options struct may be passed to the pool resource constructors
to tune the largest block size and the maximum chunk size
.A
synchronized_pool_resource may be accessed from multiple threads
without external synchronization
and may have thread-specific pools to reduce synchronization costs
. An
unsynchronized_pool_resource class may not be accessed
from multiple threads simultaneously
and thus avoids the cost of synchronization entirely
in single-threaded applications
.namespace std::pmr {
struct pool_options {
size_t max_blocks_per_chunk = 0;
size_t largest_required_pool_block = 0;
};
class synchronized_pool_resource : public memory_resource {
public:
synchronized_pool_resource(const pool_options& opts, memory_resource* upstream);
synchronized_pool_resource()
: synchronized_pool_resource(pool_options(), get_default_resource() {}
explicit synchronized_pool_resource(memory_resource* upstream)
: synchronized_pool_resource(pool_options(), upstream) {}
explicit synchronized_pool_resource(const pool_options& opts)
: synchronized_pool_resource(opts, get_default_resource() {}
synchronized_pool_resource(const synchronized_pool_resource&) = delete;
virtual ~synchronized_pool_resource();
synchronized_pool_resource& operator=(const synchronized_pool_resource&) = delete;
void release();
memory_resource* upstream_resource() const;
pool_options options() const;
protected:
void* do_allocate(size_t bytes, size_t alignment) override;
void do_deallocate(void* p, size_t bytes, size_t alignment) override;
bool do_is_equal(const memory_resource& other) const noexcept override;
};
class unsynchronized_pool_resource : public memory_resource {
public:
unsynchronized_pool_resource(const pool_options& opts, memory_resource* upstream);
unsynchronized_pool_resource()
: unsynchronized_pool_resource(pool_options(), get_default_resource() {}
explicit unsynchronized_pool_resource(memory_resource* upstream)
: unsynchronized_pool_resource(pool_options(), upstream) {}
explicit unsynchronized_pool_resource(const pool_options& opts)
: unsynchronized_pool_resource(opts, get_default_resource() {}
unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
virtual ~unsynchronized_pool_resource();
unsynchronized_pool_resource& operator=(const unsynchronized_pool_resource&) = delete;
void release();
memory_resource* upstream_resource() const;
pool_options options() const;
protected:
void* do_allocate(size_t bytes, size_t alignment) override;
void do_deallocate(void* p, size_t bytes, size_t alignment) override;
bool do_is_equal(const memory_resource& other) const noexcept override;
};
}