Skip to content

Pool holds pools at teardown

Description

Pool holds pools at teardown.

  • A carrier pool's hooks accept PoolHandle items (PoolPolyHelper.TAG).
  • Two inner pools are stored in the carrier via pool.put.
  • pool.close on the carrier walks the returned list, closes and destroys each inner pool.
  • Shows uniform cleanup of infra handles — no per-instance role discrimination needed.

Diagram

 pool.new × 2 ──► pool.put ──► carrier pool (holds inner pools as items)
      │ pool.close (carrier)
 on_close ──► pool.close + pool.destroy per inner pool

Source

pub fn pool_holds_pools_at_teardown(allocator: std.mem.Allocator, io: std.Io) !void {
    // Carrier pool — holds inner PoolHandles as items.
    const carrier: PoolHandle = try pool.new(io, allocator);
    var carrier_ctx: CarrierCtx = .{ .alloc = allocator };
    const carrier_tags = [_]*const anyopaque{PoolPolyHelper.TAG};
    try pool.init(carrier, .{
        .ctx = &carrier_ctx,
        .tags = &carrier_tags,
        .on_get = onGet,
        .on_put = onPut,
        .on_close = onClose,
    });

    const n: usize = 2;
    var ctx: Ctx = .{ .carrier = carrier, .alloc = allocator, .io = io };
    try ctx.createAndStoreInnerPools(n);
    try ctx.closeCarrier(&carrier_ctx, n);
}

const CarrierCtx = struct {
    alloc: std.mem.Allocator,
    closed_count: usize = 0,
};

fn onGet(_: *anyopaque, _: *const anyopaque, _: usize, _: *Slot) void {}

fn resetOnPut(_: *Slot) void {} // PoolHandle items carry no resettable scalar state — kept for on_put-shape consistency

fn onPut(_: *anyopaque, _: usize, slot: *Slot) void {
    resetOnPut(slot);
}

fn onClose(ctx_opaque: *anyopaque, list: *std.DoublyLinkedList) void {
    const ctx: *CarrierCtx = @ptrCast(@alignCast(ctx_opaque));
    while (list.popFirst()) |node| {
        const poly: *PolyNode = @fieldParentPtr("node", node);
        const ph: PoolHandle = poly;
        pool.close(ph);
        pool.destroy(ph, ctx.alloc);
        ctx.closed_count += 1;
    }
    std.log.info("on_close: closed and destroyed {d} inner pool(s)", .{ctx.closed_count});
}

const Ctx = struct {
    carrier: PoolHandle,
    alloc: std.mem.Allocator,
    io: std.Io,

    fn createAndStoreInnerPools(self: *Ctx, n: usize) !void {
        var j: usize = 0;
        while (j < n) : (j += 1) {
            const inner: PoolHandle = try pool.new(self.io, self.alloc);
            var slot: Slot = inner;
            pool.put(self.carrier, &slot);
            try helpers.expect(error.PoolAsItemFailed, slot == null, "carrier did not accept inner pool");
            std.log.info("stored inner pool {d} in carrier", .{j + 1});
        }
    }

    fn closeCarrier(self: *Ctx, carrier_ctx: *CarrierCtx, n: usize) !void {
        // Tag dispatch is not needed here: all items are PoolHandles by construction.
        pool.close(self.carrier);
        try helpers.expect(error.PoolAsItemFailed, carrier_ctx.closed_count == n, "wrong number of inner pools cleaned up");
        std.log.info("carrier closed: {d} inner pool(s) cleaned up", .{carrier_ctx.closed_count});
        pool.destroy(self.carrier, self.alloc);
    }
};

const helpers = @import("../helpers/helpers.zig");
const matryoshka = @import("matryoshka");
const std = @import("std");
const pool = matryoshka.pool;
const polynode = matryoshka.polynode;
const PolyNode = polynode.PolyNode;
const Slot = polynode.Slot;
const PoolHandle = pool.PoolHandle;
const PoolPolyHelper = pool.PoolPolyHelper;