authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-03-07 11:13:02+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-12 21:02:43+01:00
log4d6bef538ef1ba25b21a57b19d09a66ad4d7e4f9
treeec78866e1b85074f0f7092990405374e57f4a835
parent47597a6d7cb7bbea313c15ba4c9f5dcecb972e64

std.heap.ArenaAllocator: relax memory ordering for stealing free list

We only need acquire instead of acq_rel here, since we're always swapping in `null` there's no node whose content we'd need to release.

1 files changed, 6 insertions(+), 5 deletions(-)

lib/std/heap/ArenaAllocator.zig+6-5
...@@ -248,8 +248,8 @@ const Node = struct {...@@ -248,8 +248,8 @@ const Node = struct {
248 };248 };
249249
250 fn loadBuf(node: *Node) []u8 {250 fn loadBuf(node: *Node) []u8 {
251 // monotonic is fine since `size` can only ever grow, so the buffer returned251 // `size` can only ever grow, so the buffer returned by this function is
252 // by this function is always valid memory.252 // always valid memory.
253 const size = @atomicLoad(Size, &node.size, .monotonic);253 const size = @atomicLoad(Size, &node.size, .monotonic);
254 return @as([*]u8, @ptrCast(node))[0..size.toInt()][@sizeOf(Node)..];254 return @as([*]u8, @ptrCast(node))[0..size.toInt()][@sizeOf(Node)..];
255 }255 }
...@@ -298,8 +298,9 @@ fn tryPushNode(arena: *ArenaAllocator, node: *Node) PushResult {...@@ -298,8 +298,9 @@ fn tryPushNode(arena: *ArenaAllocator, node: *Node) PushResult {
298}298}
299299
300fn stealFreeList(arena: *ArenaAllocator) ?*Node {300fn stealFreeList(arena: *ArenaAllocator) ?*Node {
301 // syncs with acq_rel in other `stealFreeList` calls or release in `pushFreeList`301 // We don't need acq_rel here because we're always swapping in `null`, so
302 return @atomicRmw(?*Node, &arena.state.free_list, .Xchg, null, .acq_rel);302 // there's no node we'd need to release.
303 return @atomicRmw(?*Node, &arena.state.free_list, .Xchg, null, .acquire); // syncs with release in `pushFreeList`
303}304}
304305
305fn pushFreeList(arena: *ArenaAllocator, first: *Node, last: *Node) void {306fn pushFreeList(arena: *ArenaAllocator, first: *Node, last: *Node) void {
...@@ -311,7 +312,7 @@ fn pushFreeList(arena: *ArenaAllocator, first: *Node, last: *Node) void {...@@ -311,7 +312,7 @@ fn pushFreeList(arena: *ArenaAllocator, first: *Node, last: *Node) void {
311 &arena.state.free_list,312 &arena.state.free_list,
312 last.next,313 last.next,
313 first,314 first,
314 .release, // syncs with acquire part of acq_rel in `stealFreeList`315 .release, // syncs with acquire in `stealFreeList`
315 .monotonic, // we never access any fields of `old_free_list`, we only care about the pointer316 .monotonic, // we never access any fields of `old_free_list`, we only care about the pointer
316 )) |old_free_list| {317 )) |old_free_list| {
317 last.next = old_free_list;318 last.next = old_free_list;