From 255aeb57b24bc24b604744460a61ebf7c44e42ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 Feb 2026 14:57:27 -0800 Subject: [PATCH] std: introduce atomic.Mutex and use it in heap.SmpAllocator This allocator implementation uses only lock-free operations. --- lib/std/atomic.zig | 25 +++++++++++++++++++++---- lib/std/heap/SmpAllocator.zig | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/std/atomic.zig b/lib/std/atomic.zig index 0b35b61e9b..0040dbf735 100644 --- a/lib/std/atomic.zig +++ b/lib/std/atomic.zig @@ -1,3 +1,10 @@ +const builtin = @import("builtin"); + +const std = @import("std.zig"); +const AtomicOrder = std.builtin.AtomicOrder; +const testing = std.testing; +const assert = std.debug.assert; + /// This is a thin wrapper around a primitive value to prevent accidental data races. pub fn Value(comptime T: type) type { return extern struct { @@ -496,7 +503,17 @@ test "current CPU has a cache line size" { _ = cache_line; } -const std = @import("std.zig"); -const builtin = @import("builtin"); -const AtomicOrder = std.builtin.AtomicOrder; -const testing = std.testing; +/// A lock-free single-owner resource. +pub const Mutex = enum(u8) { + unlocked, + locked, + + pub fn tryLock(m: *Mutex) bool { + return @cmpxchgWeak(Mutex, m, .unlocked, .locked, .acquire, .monotonic) == null; + } + + pub fn unlock(m: *Mutex) void { + assert(m.* == .locked); + @atomicStore(Mutex, m, .unlocked, .release); + } +}; diff --git a/lib/std/heap/SmpAllocator.zig b/lib/std/heap/SmpAllocator.zig index f9637c70a2..e51e4975b6 100644 --- a/lib/std/heap/SmpAllocator.zig +++ b/lib/std/heap/SmpAllocator.zig @@ -62,7 +62,7 @@ const Thread = struct { /// /// Threads lock this before accessing their own state in order /// to support freelist reclamation. - mutex: std.Thread.Mutex = .{}, + mutex: std.atomic.Mutex = .unlocked, /// For each size class, tracks the next address to be returned from /// `alloc` when the freelist is empty.