Patterns — Pool Patterns
Concepts: Building Blocks — Pool.
API: API Reference — Pool.
Pool mode — .available_or_new
When to use.
- The common case: reuse a stored item if one is free, otherwise create a fresh one.
Code shape.
var slot: Slot = null;
defer pool.put(ph, &slot);
try pool.get(ph, EventPolyHelper.TAG, .available_or_new, &slot);
on_getruns every call. Ifslot.*is non-null it was recycled — reinitialize. If null, create.
Example: examples/layer4/018-master_with_pool.zig.
Pool mode — .new_only
When to use.
- Seeding. You want a fresh item every time, never a stored one.
Code shape.
var slot: Slot = null;
try pool.get(ph, EventPolyHelper.TAG, .new_only, &slot);
// fill the new item
pool.put(ph, &slot);
Example: examples/layer3/pool_seeding.zig.
Pool mode — .available_only
When to use.
- Consume what is stored. Stop when the pool is empty.
- Empty pool returns
error.NotAvailable— a normal end condition, not a failure.
Code shape.
var slot: Slot = null;
pool.get(ph, EventPolyHelper.TAG, .available_only, &slot) catch |err| switch (err) {
error.NotAvailable => break,
else => return err,
};
Example: examples/layer3/pool_seeding.zig.
Seeding pattern
When to use.
- A fixed-size pool. Pool capacity is set once at startup, no on-demand creation.
Code shape.
for (0..N_BUFFERS) |_| {
var slot: Slot = null;
try VideoBufferPolyHelper.create(allocator, &slot);
pool.put(ph, &slot);
}
- Pair with
on_getthat does nothing — the pool never grows past the seed count. - The fixed count becomes the backpressure limit.
Example: stories/video_transcoder/video_transcoder.zig.
Pool as lifecycle policy — on_get and on_put hooks
When to use.
on_get: decide how an item is created or reinitialized.on_put: decide whether a returned item is kept or destroyed (cap policy).
Pattern.
Code shape.
fn onGet(_: *anyopaque, _: *const anyopaque, _: usize, _: *Slot) void {} // fixed-size: never create
fn onPut(_: *anyopaque, _: usize, _: *Slot) void {} // keep all
on_put: setslot.* = nullto destroy; leave non-null to keep.- Allocation policy stays outside business logic.
Example: examples/layer3/capped_pool.zig (cap policy), examples/hooks/CappedPoolHooks.zig (thread-safe reference).
Hook outside lock
When to use.
- Shared hook state.
Code shape.
Why.
- Hooks run outside the pool lock. Multiple threads may call them at once.
- Pool does not serialize hook execution.
- Protect shared state with
Io.Mutex.lockUncancelable.
Example: examples/hooks/CappedPoolHooks.zig.
on_close hook
When to use.
- Free all stored items when the pool shuts down.
Code shape.
fn onClose(ctx: *anyopaque, list: *std.DoublyLinkedList) void {
const self: *VideoBufCtx = @ptrCast(@alignCast(ctx));
while (list.popFirst()) |node| {
const poly: *polynode.PolyNode = @fieldParentPtr("node", node);
polynode.reset(poly);
var s: Slot = poly;
VideoBufferPolyHelper.destroy(self.alloc, &s);
}
}
- Always call
polynode.reset(poly)afterpopFirstbefore destroy.
Example: examples/layer3/pool_teardown.zig, stories/video_transcoder/video_transcoder.zig.
Multi-tag pool
When to use.
- Pool stores multiple item types.
Pattern.
Why.
- One lifecycle manager.
- Separate free lists per tag.