Skip to content

Commit 728b54c

Browse files
committed
Implement TODO: Resets for mempools.
Respect arena reset result.
1 parent 617cd89 commit 728b54c

File tree

1 file changed

+50
-8
lines changed

1 file changed

+50
-8
lines changed

lib/std/heap/memory_pool.zig

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,20 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type
9595
}
9696
}
9797

98-
pub const ResetMode = std.heap.ArenaAllocator.ResetMode;
98+
pub const ResetMode = union(enum) {
99+
/// Releases all allocated memory in the arena.
100+
free_all,
101+
/// This will pre-heat the memory pool for future allocations by allocating a
102+
/// large enough buffer to accomodate the highest amount of actively allocated items
103+
/// in the past. Preheating will speed up the allocation process by invoking the
104+
/// backing allocator less often than before. If `reset()` is used in a loop, this
105+
/// means if the highest amount of actively allocated items is never being surpassed,
106+
/// no memory allocations are performed anymore.
107+
retain_capacity,
108+
/// This is the same as `retain_capacity`, but the memory will be shrunk to
109+
/// only hold at most this value of items.
110+
retain_with_limit: usize,
111+
};
99112

100113
/// Resets the memory pool and destroys all allocated items.
101114
/// This can be used to batch-destroy all objects without invalidating the memory pool.
@@ -107,14 +120,21 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type
107120
///
108121
/// NOTE: If `mode` is `free_all`, the function will always return `true`.
109122
pub fn reset(pool: *Pool, mode: ResetMode) bool {
110-
// TODO: Potentially store all allocated objects in a list as well, allowing to
111-
// just move them into the free list instead of actually releasing the memory.
112-
113-
const reset_successful = pool.arena.reset(mode);
114-
123+
const ArenaResetMode = std.heap.ArenaAllocator.ResetMode;
124+
const arena_mode = switch (mode) {
125+
.free_all => .free_all,
126+
.retain_capacity => .retain_capacity,
127+
.retain_with_limit => |limit| ArenaResetMode{ .retain_with_limit = limit * item_size },
128+
};
115129
pool.free_list = null;
116-
117-
return reset_successful;
130+
if (!pool.arena.reset(arena_mode)) return false;
131+
// When the backing arena allocator is being reset to
132+
// a capacity greater than 0, then its internals consists
133+
// of a *single* buffer node of said capacity. This means,
134+
// we can safely pre-heat without causing additional allocations.
135+
const arena_capacity = pool.arena.queryCapacity() / item_size;
136+
if (arena_capacity != 0) pool.preheat(arena_capacity) catch unreachable;
137+
return true;
118138
}
119139

120140
/// Creates a new item and adds it to the memory pool.
@@ -217,3 +237,25 @@ test "greater than pointer manual alignment" {
217237
const foo: *align(16) Foo = try pool.create();
218238
_ = foo;
219239
}
240+
241+
test "reset" {
242+
const pool_extra = MemoryPoolExtra(u32, .{ .growable = false });
243+
var pool = try pool_extra.initPreheated(std.testing.allocator, 3);
244+
defer pool.deinit();
245+
246+
try std.testing.expect(pool.create() != error.OutOfMemory);
247+
try std.testing.expect(pool.create() != error.OutOfMemory);
248+
try std.testing.expect(pool.create() != error.OutOfMemory);
249+
try std.testing.expect(pool.create() == error.OutOfMemory);
250+
251+
try std.testing.expect(pool.reset(.{ .retain_with_limit = 2 }));
252+
253+
try std.testing.expect(pool.create() != error.OutOfMemory);
254+
try std.testing.expect(pool.create() != error.OutOfMemory);
255+
try std.testing.expect(pool.create() == error.OutOfMemory);
256+
257+
try std.testing.expect(pool.reset(.{ .retain_with_limit = 1 }));
258+
259+
try std.testing.expect(pool.create() != error.OutOfMemory);
260+
try std.testing.expect(pool.create() == error.OutOfMemory);
261+
}

0 commit comments

Comments
 (0)