Pool structv0.1.0
pub struct Pool {
// private fields
}
Move-only, because it owns its chunks. Pass it to something expecting an Allocator through Handle, which
borrows rather than moves.
Constructors
Pool
pub func Pool(
backing: Allocator,
blocksPerChunk: uint
) -> Pool
A pool that will take its chunks from backing.
backing is borrowed for the call and kept. blocksPerChunk is how many blocks each chunk is carved into;
zero asks for the default. Cannot fail and allocates nothing: the first chunk is taken when the first request
arrives, so a pool that is never used costs nothing.
| Name | Type | Description |
|---|---|---|
backing | Allocator | where the pool's chunks come from |
blocksPerChunk | uint | how many blocks a chunk carves into; zero asks for the default |
Returns: the pool, holding no chunk yet
Methods
Handle
pub func Handle(self: *var Pool) -> PoolHandle
A copyable interface adapter pointing at this pool.
self stays the caller's. Allocations through the handle update this pool rather than copied state.
Cannot fail. Runs in constant time and allocates nothing.
The handle borrows the pool and must not outlive it, nor survive the pool being moved.
Returns: a copyable adapter pointing at this pool
Reset
pub func Reset(self: &var Pool)
Takes every pooled block back at once, keeping the chunks for the next round of work.
self is borrowed mutably. Every pooled pointer the pool handed out becomes invalid, including any a caller
still holds. The chunks are kept and carved again, so a second round of the same work asks the backing
allocator for nothing.
Blocks that went to the backing allocator because they were too large or too strictly aligned are not
touched, and are still the caller's to release.
Runs in time proportional to the number of blocks. Cannot fail.
Nothing may be read from or written to any pooled allocation afterwards.
LiveBlocks
pub func LiveBlocks(self: &Pool) -> uint
How many pooled blocks are currently handed out.
self is borrowed. Blocks that went to the backing allocator are not counted, because the pool does not
track them.
Returns: how many blocks are currently handed out
ChunkCount
pub func ChunkCount(self: &Pool) -> uint
How many chunks the pool has taken from its backing allocator.
self is borrowed. Zero before the first request.
Returns: how many chunks the pool holds
ClassSize
pub func ClassSize(self: &Pool, index: uint) -> uint
The block size of class index, or zero for an index past the last class.
self is borrowed. Classes run from PoolSmallestClass to PoolLargestClass, doubling.
| Name | Type | Description |
|---|---|---|
index | uint | which size class |
Returns: the block size that class serves
Destructor
func ~Pool(self: &var Pool)
Gives every chunk back to the backing allocator. Runs when the pool's life ends. Every pooled pointer the pool handed out becomes invalid. Blocks that went to the backing allocator directly are not released here and are still the caller's.
Related
PoolClassCount
pub const PoolClassCount: uint = 5;
How many size classes a pool serves.
PoolSmallestClass
pub const PoolSmallestClass: uint = 16;
The smallest size class, and so the block a one-byte request occupies. Large enough to hold the link a free block stores in its own storage, which is what lets a free list cost nothing beyond the blocks already on it.
PoolLargestClass
pub const PoolLargestClass: uint = 256;
The largest size class. A request past this goes to the backing allocator instead.
PoolAlignment
pub const PoolAlignment: uint = 16;
The alignment every chunk is taken at, and so the strictest alignment a pooled block can satisfy. Every class is a multiple of this, so every block carved from a chunk is aligned to it as well.
DefaultPoolBlocksPerChunk
pub const DefaultPoolBlocksPerChunk: uint = 32;
How many blocks a chunk holds when the caller does not say.
PoolChunk
pub struct PoolChunk {
// private fields
}
One chunk of pool storage, carved into blocks of a single class. Never constructed by a caller. The blocks follow the header, and the header records what the chunk was taken with so that releasing or re-carving it needs nothing else.
PoolHandle
pub struct PoolHandle {
// private fields
}
A copyable interface adapter pointing at a pool. A handle is cheap to copy and may be stored wherever an allocator is wanted, but it does not own the pool.
Implements Allocator
PoolHandle conforms to Allocator.
Allocate
pub func Allocate(
self: &PoolHandle,
layout: Layout
) -> (*var opaque) ! AllocError
Takes a block of the smallest class that holds layout.
result receives the address when this reports None. A zero-sized layout gives the shared empty block. A
layout too large for the largest class, or needing an alignment stricter than PoolAlignment, is passed to
the backing allocator unchanged.
Takes a chunk when the class has no free block, which is the only path here that can fail. Otherwise runs in
constant time.
The block lives until it is released, the pool is reset, or the pool is destroyed.
| Name | Type | Description |
|---|---|---|
layout | Layout | the size and alignment wanted |
Returns: the address of the storage, or why none was taken
Deallocate
pub func Deallocate(
self: &PoolHandle,
block: *var opaque,
layout: Layout
) -> ! AllocError
Puts a block back on the free list of the class its layout names.
layout must equal the one the block was taken with, which is what tells the pool whether the block is its
own or the backing allocator's. A block that went to the backing allocator is released there.
Runs in constant time, and cannot fail for a pooled block.
The block may be handed to the next allocation of its class at once.
| Name | Type | Description |
|---|---|---|
block | *var opaque | an address this allocator returned |
layout | Layout | the layout it was allocated with |
Returns: nothing when the release was accepted, or why it was refused
Reallocate
pub func Reallocate(
self: &PoolHandle,
block: *var opaque,
oldLayout: Layout,
newLayout: Layout
) -> (*var opaque) ! AllocError
Changes the size of a block, which costs nothing when both sizes land in the same class. The two layouts must share an alignment. A block whose new size fits the class it already occupies comes back unmoved — growing a 20-byte allocation to 30 does not touch storage at all. Anything else takes a new block, copies, and releases the old one.
| Name | Type | Description |
|---|---|---|
block | *var opaque | the existing allocation's address |
oldLayout | Layout | the layout block was allocated with |
newLayout | Layout | the layout wanted, which must have the same alignment |
Returns: the address the block is at afterwards, or why it was not resized
ClassIndexFor
pub func ClassIndexFor(layout: Layout) -> uint
The class a layout belongs to, or PoolClassCount for a layout no class can serve.
layout is read by value. A size past PoolLargestClass, or an alignment stricter than PoolAlignment, has no
class, and the caller passes it to the backing allocator instead.
The answer depends only on the layout, which is what lets a release reach the same conclusion as the allocation
it matches without the pool recording anything per block.
Cannot fail. Runs in time proportional to the number of classes and allocates nothing.
Parameters
| Name | Type | Description |
|---|---|---|
layout | Layout | the request to classify |
Returns: the index of the class that serves it, or the class count when none does