authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2025-08-13 19:13:43+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-11-15 09:30:57+00:00
log4187d0e8feda7982c1d15a73e99a42cbf4e355e3
tree2c23d2a69a7acf6252435346895db998e7dab0cb
parent250803661c2a3642c413e0ebb25475605093873e

MemoryPool: add unmanaged variants and make them the default


3 files changed, 281 insertions(+), 113 deletions(-)

lib/compiler/resinator/source_mapping.zig+4-4
...@@ -723,7 +723,7 @@ pub const SourceMappings = struct {...@@ -723,7 +723,7 @@ pub const SourceMappings = struct {
723 /// The default assumes that the first filename added is the root file.723 /// The default assumes that the first filename added is the root file.
724 /// The value should be set to the correct offset if that assumption does not hold.724 /// The value should be set to the correct offset if that assumption does not hold.
725 root_filename_offset: u32 = 0,725 root_filename_offset: u32 = 0,
726 source_node_pool: std.heap.MemoryPool(Sources.Node) = std.heap.MemoryPool(Sources.Node).init(std.heap.page_allocator),726 source_node_pool: std.heap.MemoryPool(Sources.Node) = .empty,
727 end_line: usize = 0,727 end_line: usize = 0,
728728
729 const sourceCompare = struct {729 const sourceCompare = struct {
...@@ -742,7 +742,7 @@ pub const SourceMappings = struct {...@@ -742,7 +742,7 @@ pub const SourceMappings = struct {
742742
743 pub fn deinit(self: *SourceMappings, allocator: Allocator) void {743 pub fn deinit(self: *SourceMappings, allocator: Allocator) void {
744 self.files.deinit(allocator);744 self.files.deinit(allocator);
745 self.source_node_pool.deinit();745 self.source_node_pool.deinit(std.heap.page_allocator);
746 }746 }
747747
748 /// Find the node that 'contains' the `line`, i.e. the node's start_line is748 /// Find the node that 'contains' the `line`, i.e. the node's start_line is
...@@ -823,7 +823,7 @@ pub const SourceMappings = struct {...@@ -823,7 +823,7 @@ pub const SourceMappings = struct {
823 .filename_offset = filename_offset,823 .filename_offset = filename_offset,
824 };824 };
825 var entry = self.sources.getEntryFor(key);825 var entry = self.sources.getEntryFor(key);
826 var new_node = try self.source_node_pool.create();826 var new_node = try self.source_node_pool.create(std.heap.page_allocator);
827 new_node.key = key;827 new_node.key = key;
828 entry.set(new_node);828 entry.set(new_node);
829 }829 }
...@@ -869,7 +869,7 @@ pub const SourceMappings = struct {...@@ -869,7 +869,7 @@ pub const SourceMappings = struct {
869 .filename_offset = node.key.filename_offset,869 .filename_offset = node.key.filename_offset,
870 };870 };
871 var entry = self.sources.getEntryFor(key);871 var entry = self.sources.getEntryFor(key);
872 var new_node = try self.source_node_pool.create();872 var new_node = try self.source_node_pool.create(std.heap.page_allocator);
873 new_node.key = key;873 new_node.key = key;
874 entry.set(new_node);874 entry.set(new_node);
875 node = new_node;875 node = new_node;
lib/std/heap.zig+14-4
...@@ -25,10 +25,20 @@ pub const GeneralPurposeAllocatorConfig = DebugAllocatorConfig;...@@ -25,10 +25,20 @@ pub const GeneralPurposeAllocatorConfig = DebugAllocatorConfig;
25/// Deprecated; to be removed after 0.14.0 is tagged.25/// Deprecated; to be removed after 0.14.0 is tagged.
26pub const GeneralPurposeAllocator = DebugAllocator;26pub const GeneralPurposeAllocator = DebugAllocator;
2727
28const memory_pool = @import("heap/memory_pool.zig");28/// A memory pool that can allocate objects of a single type very quickly.
29pub const MemoryPool = memory_pool.MemoryPool;29/// Use this when you need to allocate a lot of objects of the same type,
30pub const MemoryPoolAligned = memory_pool.MemoryPoolAligned;30/// because it outperforms general purpose allocators.
31pub const MemoryPoolExtra = memory_pool.MemoryPoolExtra;31/// Functions that potentially allocate memory accept an `Allocator` parameter.
32pub fn MemoryPool(comptime Item: type) type {
33 return memory_pool.Extra(Item, .{ .alignment = null });
34}
35pub const memory_pool = @import("heap/memory_pool.zig");
36
37/// Deprecated; use `memory_pool.Aligned`.
38pub const MemoryPoolAligned = memory_pool.Aligned;
39/// Deprecated; use `memory_pool.Extra`.
40pub const MemoryPoolExtra = memory_pool.Extra;
41/// Deprecated; use `memory_pool.Options`.
32pub const MemoryPoolOptions = memory_pool.Options;42pub const MemoryPoolOptions = memory_pool.Options;
3343
34/// TODO Utilize this on Windows.44/// TODO Utilize this on Windows.
lib/std/heap/memory_pool.zig+263-105
...@@ -1,26 +1,26 @@...@@ -1,26 +1,26 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const Allocator = std.mem.Allocator;
2const Alignment = std.mem.Alignment;3const Alignment = std.mem.Alignment;
4const MemoryPool = std.heap.MemoryPool;
35
4const debug_mode = @import("builtin").mode == .Debug;6/// Deprecated.
57pub fn Managed(comptime Item: type) type {
6pub const MemoryPoolError = error{OutOfMemory};8 return ExtraManaged(Item, .{ .alignment = null });
9}
710
8/// A memory pool that can allocate objects of a single type very quickly.11/// A memory pool that can allocate objects of a single type very quickly.
9/// Use this when you need to allocate a lot of objects of the same type,12/// Use this when you need to allocate a lot of objects of the same type,
10/// because It outperforms general purpose allocators.13/// because it outperforms general purpose allocators.
11pub fn MemoryPool(comptime Item: type) type {14/// Allocated items are aligned to `alignment`-byte addresses or `@alignOf(Item)`
12 return MemoryPoolAligned(Item, .of(Item));15/// if `alignment` is `null`.
16/// Functions that potentially allocate memory accept an `Allocator` parameter.
17pub fn Aligned(comptime Item: type, comptime alignment: Alignment) type {
18 return Extra(Item, .{ .alignment = alignment });
13}19}
1420
15/// A memory pool that can allocate objects of a single type very quickly.21/// Deprecated.
16/// Use this when you need to allocate a lot of objects of the same type,22pub fn AlignedManaged(comptime Item: type, comptime alignment: Alignment) type {
17/// because It outperforms general purpose allocators.23 return ExtraManaged(Item, .{ .alignment = alignment });
18pub fn MemoryPoolAligned(comptime Item: type, comptime alignment: Alignment) type {
19 if (@alignOf(Item) == comptime alignment.toByteUnits()) {
20 return MemoryPoolExtra(Item, .{});
21 } else {
22 return MemoryPoolExtra(Item, .{ .alignment = alignment });
23 }
24}24}
2525
26pub const Options = struct {26pub const Options = struct {
...@@ -34,64 +34,70 @@ pub const Options = struct {...@@ -34,64 +34,70 @@ pub const Options = struct {
3434
35/// A memory pool that can allocate objects of a single type very quickly.35/// A memory pool that can allocate objects of a single type very quickly.
36/// Use this when you need to allocate a lot of objects of the same type,36/// Use this when you need to allocate a lot of objects of the same type,
37/// because It outperforms general purpose allocators.37/// because it outperforms general purpose allocators.
38pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type {38/// Functions that potentially allocate memory accept an `Allocator` parameter.
39pub fn Extra(comptime Item: type, comptime pool_options: Options) type {
40 if (pool_options.alignment) |a| {
41 if (a.compare(.eq, .of(Item))) {
42 var new_options = pool_options;
43 new_options.alignment = null;
44 return Extra(Item, new_options);
45 }
46 }
39 return struct {47 return struct {
40 const Pool = @This();48 const Pool = @This();
4149
50 arena_state: std.heap.ArenaAllocator.State,
51 free_list: std.SinglyLinkedList,
52
42 /// Size of the memory pool items. This is not necessarily the same53 /// Size of the memory pool items. This is not necessarily the same
43 /// as `@sizeOf(Item)` as the pool also uses the items for internal means.54 /// as `@sizeOf(Item)` as the pool also uses the items for internal means.
44 pub const item_size = @max(@sizeOf(Node), @sizeOf(Item));55 pub const item_size = @max(@sizeOf(Node), @sizeOf(Item));
4556
46 // This needs to be kept in sync with Node.
47 const node_alignment: Alignment = .of(*anyopaque);
48
49 /// Alignment of the memory pool items. This is not necessarily the same57 /// Alignment of the memory pool items. This is not necessarily the same
50 /// as `@alignOf(Item)` as the pool also uses the items for internal means.58 /// as `@alignOf(Item)` as the pool also uses the items for internal means.
51 pub const item_alignment: Alignment = node_alignment.max(pool_options.alignment orelse .of(Item));59 pub const item_alignment: Alignment = .max(pool_options.alignment orelse .of(Item), .of(Node));
5260
53 const Node = struct {61 const Node = std.SinglyLinkedList.Node;
54 next: ?*align(item_alignment.toByteUnits()) @This(),
55 };
56 const NodePtr = *align(item_alignment.toByteUnits()) Node;
57 const ItemPtr = *align(item_alignment.toByteUnits()) Item;62 const ItemPtr = *align(item_alignment.toByteUnits()) Item;
5863
59 arena: std.heap.ArenaAllocator,64 /// A MemoryPool containing no elements.
60 free_list: ?NodePtr = null,65 pub const empty: Pool = .{
6166 .arena_state = .{},
62 /// Creates a new memory pool.67 .free_list = .{},
63 pub fn init(allocator: std.mem.Allocator) Pool {68 };
64 return .{ .arena = std.heap.ArenaAllocator.init(allocator) };
65 }
6669
67 /// Creates a new memory pool and pre-allocates `initial_size` items.70 /// Creates a new memory pool and pre-allocates `num` items.
68 /// This allows the up to `initial_size` active allocations before a71 /// This allows up to `num` active allocations before an
69 /// `OutOfMemory` error happens when calling `create()`.72 /// `OutOfMemory` error might happen when calling `create()`.
70 pub fn initPreheated(allocator: std.mem.Allocator, initial_size: usize) MemoryPoolError!Pool {73 pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!Pool {
71 var pool = init(allocator);74 var pool: Pool = .empty;
72 errdefer pool.deinit();75 errdefer pool.deinit(allocator);
73 try pool.preheat(initial_size);76 try pool.addCapacity(allocator, num);
74 return pool;77 return pool;
75 }78 }
7679
77 /// Destroys the memory pool and frees all allocated memory.80 /// Destroys the memory pool and frees all allocated memory.
78 pub fn deinit(pool: *Pool) void {81 pub fn deinit(pool: *Pool, allocator: Allocator) void {
79 pool.arena.deinit();82 pool.arena_state.promote(allocator).deinit();
80 pool.* = undefined;83 pool.* = undefined;
81 }84 }
8285
83 /// Preheats the memory pool by pre-allocating `size` items.86 pub fn toManaged(pool: Pool, allocator: Allocator) ExtraManaged(Item, pool_options) {
84 /// This allows up to `size` active allocations before an87 return .{
88 .allocator = allocator,
89 .unmanaged = pool,
90 };
91 }
92
93 /// Pre-allocates `num` items and adds them to the memory pool.
94 /// This allows at least `num` active allocations before an
85 /// `OutOfMemory` error might happen when calling `create()`.95 /// `OutOfMemory` error might happen when calling `create()`.
86 pub fn preheat(pool: *Pool, size: usize) MemoryPoolError!void {96 pub fn addCapacity(pool: *Pool, allocator: Allocator, num: usize) Allocator.Error!void {
87 var i: usize = 0;97 var i: usize = 0;
88 while (i < size) : (i += 1) {98 while (i < num) : (i += 1) {
89 const raw_mem = try pool.allocNew();99 const memory = try pool.allocNew(allocator);
90 const free_node = @as(NodePtr, @ptrCast(raw_mem));100 pool.free_list.prepend(@ptrCast(memory));
91 free_node.* = Node{
92 .next = pool.free_list,
93 };
94 pool.free_list = free_node;
95 }101 }
96 }102 }
97103
...@@ -106,28 +112,29 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type...@@ -106,28 +112,29 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type
106 /// be slower.112 /// be slower.
107 ///113 ///
108 /// NOTE: If `mode` is `free_all`, the function will always return `true`.114 /// NOTE: If `mode` is `free_all`, the function will always return `true`.
109 pub fn reset(pool: *Pool, mode: ResetMode) bool {115 pub fn reset(pool: *Pool, allocator: Allocator, mode: ResetMode) bool {
110 // TODO: Potentially store all allocated objects in a list as well, allowing to116 // 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.117 // just move them into the free list instead of actually releasing the memory.
112118
113 const reset_successful = pool.arena.reset(mode);119 var arena = pool.arena_state.promote(allocator);
120 defer pool.arena_state = arena.state;
114121
115 pool.free_list = null;122 const reset_successful = arena.reset(mode);
123 pool.free_list = .{};
116124
117 return reset_successful;125 return reset_successful;
118 }126 }
119127
120 /// Creates a new item and adds it to the memory pool.128 /// Creates a new item and adds it to the memory pool.
121 pub fn create(pool: *Pool) !ItemPtr {129 /// `allocator` may be `undefined` if pool is not `growable`.
122 const node = if (pool.free_list) |item| blk: {130 pub fn create(pool: *Pool, allocator: Allocator) Allocator.Error!ItemPtr {
123 pool.free_list = item.next;131 const ptr: ItemPtr = if (pool.free_list.popFirst()) |node|
124 break :blk item;132 @ptrCast(@alignCast(node))
125 } else if (pool_options.growable)133 else if (pool_options.growable)
126 @as(NodePtr, @ptrCast(try pool.allocNew()))134 @ptrCast(try pool.allocNew(allocator))
127 else135 else
128 return error.OutOfMemory;136 return error.OutOfMemory;
129137
130 const ptr = @as(ItemPtr, @ptrCast(node));
131 ptr.* = undefined;138 ptr.* = undefined;
132 return ptr;139 return ptr;
133 }140 }
...@@ -136,87 +143,238 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type...@@ -136,87 +143,238 @@ pub fn MemoryPoolExtra(comptime Item: type, comptime pool_options: Options) type
136 /// Only pass items to `ptr` that were previously created with `create()` of the same memory pool!143 /// Only pass items to `ptr` that were previously created with `create()` of the same memory pool!
137 pub fn destroy(pool: *Pool, ptr: ItemPtr) void {144 pub fn destroy(pool: *Pool, ptr: ItemPtr) void {
138 ptr.* = undefined;145 ptr.* = undefined;
146 pool.free_list.prepend(@ptrCast(ptr));
147 }
139148
140 const node = @as(NodePtr, @ptrCast(ptr));149 fn allocNew(pool: *Pool, allocator: Allocator) Allocator.Error!*align(item_alignment.toByteUnits()) [item_size]u8 {
141 node.* = Node{150 var arena = pool.arena_state.promote(allocator);
142 .next = pool.free_list,151 defer pool.arena_state = arena.state;
143 };152 const memory = try arena.allocator().alignedAlloc(u8, item_alignment, item_size);
144 pool.free_list = node;153 return memory[0..item_size];
154 }
155 };
156}
157
158/// Deprecated.
159pub fn ExtraManaged(comptime Item: type, comptime pool_options: Options) type {
160 if (pool_options.alignment) |a| {
161 if (a.compare(.eq, .of(Item))) {
162 var new_options = pool_options;
163 new_options.alignment = null;
164 return ExtraManaged(Item, new_options);
165 }
166 }
167 return struct {
168 const Pool = @This();
169
170 allocator: Allocator,
171 unmanaged: Unmanaged,
172
173 pub const Unmanaged = Extra(Item, pool_options);
174 pub const item_size = Unmanaged.item_size;
175 pub const item_alignment = Unmanaged.item_alignment;
176
177 const ItemPtr = Unmanaged.ItemPtr;
178
179 /// Creates a new memory pool.
180 pub fn init(allocator: Allocator) Pool {
181 return Unmanaged.empty.toManaged(allocator);
182 }
183
184 /// Creates a new memory pool and pre-allocates `num` items.
185 /// This allows up to `num` active allocations before an
186 /// `OutOfMemory` error might happen when calling `create()`.
187 pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!Pool {
188 return (try Unmanaged.initCapacity(allocator, num)).toManaged(allocator);
189 }
190
191 /// Destroys the memory pool and frees all allocated memory.
192 pub fn deinit(pool: *Pool) void {
193 pool.unmanaged.deinit(pool.allocator);
194 pool.* = undefined;
195 }
196
197 /// Pre-allocates `num` items and adds them to the memory pool.
198 /// This allows at least `num` active allocations before an
199 /// `OutOfMemory` error might happen when calling `create()`.
200 pub fn addCapacity(pool: *Pool, num: usize) Allocator.Error!void {
201 return pool.unmanaged.addCapacity(pool.allocator, num);
202 }
203
204 pub const ResetMode = Unmanaged.ResetMode;
205
206 /// Resets the memory pool and destroys all allocated items.
207 /// This can be used to batch-destroy all objects without invalidating the memory pool.
208 ///
209 /// The function will return whether the reset operation was successful or not.
210 /// If the reallocation failed `false` is returned. The pool will still be fully
211 /// functional in that case, all memory is released. Future allocations just might
212 /// be slower.
213 ///
214 /// NOTE: If `mode` is `free_all`, the function will always return `true`.
215 pub fn reset(pool: *Pool, mode: ResetMode) bool {
216 return pool.unmanaged.reset(pool.allocator, mode);
145 }217 }
146218
147 fn allocNew(pool: *Pool) MemoryPoolError!*align(item_alignment.toByteUnits()) [item_size]u8 {219 /// Creates a new item and adds it to the memory pool.
148 const mem = try pool.arena.allocator().alignedAlloc(u8, item_alignment, item_size);220 pub fn create(pool: *Pool) Allocator.Error!ItemPtr {
149 return mem[0..item_size]; // coerce slice to array pointer221 return pool.unmanaged.create(pool.allocator);
222 }
223
224 /// Destroys a previously created item.
225 /// Only pass items to `ptr` that were previously created with `create()` of the same memory pool!
226 pub fn destroy(pool: *Pool, ptr: ItemPtr) void {
227 return pool.unmanaged.destroy(ptr);
228 }
229
230 fn allocNew(pool: *Pool) Allocator.Error!*align(item_alignment) [item_size]u8 {
231 return pool.unmanaged.allocNew(pool.allocator);
150 }232 }
151 };233 };
152}234}
153235
154test "basic" {236test "basic" {
155 var pool = MemoryPool(u32).init(std.testing.allocator);237 const a = std.testing.allocator;
156 defer pool.deinit();238
239 {
240 var pool: MemoryPool(u32) = .empty;
241 defer pool.deinit(a);
157242
158 const p1 = try pool.create();243 const p1 = try pool.create(a);
159 const p2 = try pool.create();244 const p2 = try pool.create(a);
160 const p3 = try pool.create();245 const p3 = try pool.create(a);
161246
162 // Assert uniqueness247 // Assert uniqueness
163 try std.testing.expect(p1 != p2);248 try std.testing.expect(p1 != p2);
164 try std.testing.expect(p1 != p3);249 try std.testing.expect(p1 != p3);
165 try std.testing.expect(p2 != p3);250 try std.testing.expect(p2 != p3);
251
252 pool.destroy(p2);
253 const p4 = try pool.create(a);
254
255 // Assert memory reuse
256 try std.testing.expect(p2 == p4);
257 }
166258
167 pool.destroy(p2);259 {
168 const p4 = try pool.create();260 var pool: Managed(u32) = .init(std.testing.allocator);
261 defer pool.deinit();
169262
170 // Assert memory reuse263 const p1 = try pool.create();
171 try std.testing.expect(p2 == p4);264 const p2 = try pool.create();
265 const p3 = try pool.create();
266
267 // Assert uniqueness
268 try std.testing.expect(p1 != p2);
269 try std.testing.expect(p1 != p3);
270 try std.testing.expect(p2 != p3);
271
272 pool.destroy(p2);
273 const p4 = try pool.create();
274
275 // Assert memory reuse
276 try std.testing.expect(p2 == p4);
277 }
172}278}
173279
174test "preheating (success)" {280test "initCapacity (success)" {
175 var pool = try MemoryPool(u32).initPreheated(std.testing.allocator, 4);281 const a = std.testing.allocator;
176 defer pool.deinit();282
283 {
284 var pool: MemoryPool(u32) = try .initCapacity(a, 4);
285 defer pool.deinit(a);
286
287 _ = try pool.create(a);
288 _ = try pool.create(a);
289 _ = try pool.create(a);
290 }
291
292 {
293 var pool: Managed(u32) = try .initCapacity(a, 4);
294 defer pool.deinit();
177295
178 _ = try pool.create();296 _ = try pool.create();
179 _ = try pool.create();297 _ = try pool.create();
180 _ = try pool.create();298 _ = try pool.create();
299 }
181}300}
182301
183test "preheating (failure)" {302test "initCapacity (failure)" {
184 const failer = std.testing.failing_allocator;303 const failer = std.testing.failing_allocator;
185 try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initPreheated(failer, 5));304 try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initCapacity(failer, 5));
305 try std.testing.expectError(error.OutOfMemory, Managed(u32).initCapacity(failer, 5));
186}306}
187307
188test "growable" {308test "growable" {
189 var pool = try MemoryPoolExtra(u32, .{ .growable = false }).initPreheated(std.testing.allocator, 4);309 const a = std.testing.allocator;
190 defer pool.deinit();
191310
192 _ = try pool.create();311 {
193 _ = try pool.create();312 var pool: Extra(u32, .{ .growable = false }) = try .initCapacity(a, 4);
194 _ = try pool.create();313 defer pool.deinit(a);
195 _ = try pool.create();314
315 _ = try pool.create(a);
316 _ = try pool.create(a);
317 _ = try pool.create(a);
318 _ = try pool.create(a);
319
320 try std.testing.expectError(error.OutOfMemory, pool.create(a));
321 }
196322
197 try std.testing.expectError(error.OutOfMemory, pool.create());323 {
324 var pool: ExtraManaged(u32, .{ .growable = false }) = try .initCapacity(a, 4);
325 defer pool.deinit();
326
327 _ = try pool.create();
328 _ = try pool.create();
329 _ = try pool.create();
330 _ = try pool.create();
331
332 try std.testing.expectError(error.OutOfMemory, pool.create());
333 }
198}334}
199335
200test "greater than pointer default alignment" {336test "greater than pointer default alignment" {
201 const Foo = struct {337 const Foo = struct {
202 data: u64 align(16),338 data: u64 align(16),
203 };339 };
340 const a = std.testing.allocator;
341
342 {
343 var pool: MemoryPool(Foo) = .empty;
344 defer pool.deinit(a);
345
346 const foo: *Foo = try pool.create(a);
347 pool.destroy(foo);
348 }
204349
205 var pool = MemoryPool(Foo).init(std.testing.allocator);350 {
206 defer pool.deinit();351 var pool: Managed(Foo) = .init(a);
352 defer pool.deinit();
207353
208 const foo: *Foo = try pool.create();354 const foo: *Foo = try pool.create();
209 _ = foo;355 pool.destroy(foo);
356 }
210}357}
211358
212test "greater than pointer manual alignment" {359test "greater than pointer manual alignment" {
213 const Foo = struct {360 const Foo = struct {
214 data: u64,361 data: u64,
215 };362 };
363 const a = std.testing.allocator;
364
365 {
366 var pool: Aligned(Foo, .@"16") = .empty;
367 defer pool.deinit(a);
216368
217 var pool = MemoryPoolAligned(Foo, .@"16").init(std.testing.allocator);369 const foo: *align(16) Foo = try pool.create(a);
218 defer pool.deinit();370 pool.destroy(foo);
371 }
219372
220 const foo: *align(16) Foo = try pool.create();373 {
221 _ = foo;374 var pool: AlignedManaged(Foo, .@"16") = .init(a);
375 defer pool.deinit();
376
377 const foo: *align(16) Foo = try pool.create();
378 pool.destroy(foo);
379 }
222}380}