Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions v2/lib/collections/pool.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ const util = @import("../util.zig");
/// // This storage would typically be a shared memory mapping. Stack storage used here
/// // for simplicity.
/// var storage: [IntPool.size()]u8 align(@alignOf(IntPool)) = undefined;
/// const pool: *IntPool = @ptrCast(&storage);
/// pool.init();
/// const pool = IntPool.initInBuffer(&storage);
///
/// const item_id = try pool.createId();
/// item_id.ptr(pool).* = 42;
Expand All @@ -44,11 +43,12 @@ pub fn SharedPool(Item: type, cap: usize) type {
free_list: ItemId.Optional,

// This is effectively a [capacity]Node, however Zig doesn't let us make structs over 4GiB,
// which some pools may be.
// which some pools may be. A zero length array adds nothing to the header, so it just
// marks where the items start and forces the header to be padded to their alignment.
// We're not using a pointer as we want the header field(s) to be in the same buffer as the
// pool items.
// This should normally only be accessed via .buf().
_buf: void align(@alignOf(Node)),
_buf: [0]Node,

const PoolSelf = @This();
pub const capacity = cap;
Expand Down Expand Up @@ -95,14 +95,23 @@ pub fn SharedPool(Item: type, cap: usize) type {
return @offsetOf(PoolSelf, "_buf") + capacity * @sizeOf(Item);
}

/// Places an initialized pool at the start of `buffer`, which must hold size() bytes.
pub fn initInBuffer(buffer: []align(@alignOf(PoolSelf)) u8) *PoolSelf {
std.debug.assert(buffer.len >= size());

const pool: *PoolSelf = @ptrCast(buffer.ptr);
pool.init();
return pool;
}

/// Only use this if you're certain that it's the only way
pub fn buf(pool: *PoolSelf) []Node {
const base: [*]Node = @ptrCast(&pool._buf);
const base: [*]Node = &pool._buf;
return base[0..capacity];
}

pub fn constBuf(pool: *const PoolSelf) []const Node {
const base: [*]const Node = @ptrCast(&pool._buf);
const base: [*]const Node = &pool._buf;
return base[0..capacity];
}

Expand Down Expand Up @@ -324,6 +333,35 @@ pub fn Pool(Item: type, IdInt: type) type {
};
}

test "shared pool layout" {
const P = SharedPool(u64, 4);

// the zero length array must add no size to the header, while still aligning it to the items
try std.testing.expectEqual(@sizeOf(P), @offsetOf(P, "_buf"));
try std.testing.expectEqual(@alignOf(u64), @alignOf(P));
try std.testing.expectEqual(@sizeOf(P) + 4 * @sizeOf(u64), P.size());
}

test "shared pool create + destroy" {
const capacity = 4;
const P = SharedPool(u64, capacity);

var storage: [P.size()]u8 align(@alignOf(P)) = undefined;
const pool = P.initInBuffer(&storage);

var ids: [capacity]P.ItemId = undefined;
for (&ids, 0..) |*id, i| {
id.* = try pool.createId();
id.ptr(pool).* = i;
}
try std.testing.expectError(error.OutOfSpace, pool.createId());

for (ids, 0..) |id, i| try std.testing.expectEqual(i, id.constPtr(pool).*);

pool.destroyId(ids[1]);
try std.testing.expectEqual(ids[1], try pool.createId());
}

test "pool create + destroy" {
const capacity = 2048;
const allocator = std.testing.allocator;
Expand Down
6 changes: 2 additions & 4 deletions v2/services/replay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1625,8 +1625,7 @@ test "MerkleForest tree put" {
};

var pool_buf: [api.BlockPool.size()]u8 align(@alignOf(api.BlockPool)) = undefined;
const pool: *api.BlockPool = @ptrCast(&pool_buf);
pool.init();
const pool = api.BlockPool.initInBuffer(&pool_buf);

const logger = tel.Logger("main").noop;

Expand Down Expand Up @@ -1705,8 +1704,7 @@ test "bootstrap creates root block and chains blockhashes" {
metadata.populateSlot(root_slot);

var pool_buf: [api.BlockPool.size()]u8 align(@alignOf(api.BlockPool)) = undefined;
const pool: *api.BlockPool = @ptrCast(&pool_buf);
pool.init();
const pool = api.BlockPool.initInBuffer(&pool_buf);

var forest: MerkleForest = try .init(allocator);
defer forest.deinit(allocator);
Expand Down