From cf48041b55fbc2df0a2e12ae2a9490fc39e11486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 24 Jan 2026 05:30:27 +0100 Subject: [PATCH] std.Thread.Condition: use pthread_cond_t impl when OS has no futex primitive Same principle as #30835. --- lib/std/Thread/Condition.zig | 65 ++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 788b24038e..8917e07a4f 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -107,12 +107,30 @@ pub fn broadcast(self: *Condition) void { self.impl.wake(.all); } -const Impl = if (builtin.single_threaded) - SingleThreadedImpl -else if (builtin.os.tag == .windows) - WindowsImpl -else - FutexImpl; +const Impl = Impl: { + if (builtin.single_threaded) break :Impl SingleThreadedImpl; + if (builtin.os.tag == .windows) break :Impl WindowsImpl; + + if (builtin.os.tag.isDarwin() or + builtin.target.os.tag == .linux or + builtin.target.os.tag == .freebsd or + builtin.target.os.tag == .openbsd or + builtin.target.os.tag == .dragonfly or + builtin.target.cpu.arch.isWasm()) + { + // Futex is the system's synchronization primitive; use that. + break :Impl FutexImpl; + } + + if (std.Thread.use_pthreads) { + // This system doesn't have a futex primitive, so `std.Thread.Futex` is using `PosixImpl`, + // which implements futex *on top of* pthread mutexes and conditions. Therefore, instead + // of going through that long inefficient path, just use pthread condition variable directly. + break :Impl PosixImpl; + } + + break :Impl FutexImpl; +}; const Notify = enum { one, // wake up only one thread @@ -291,6 +309,41 @@ const FutexImpl = struct { } }; +const PosixImpl = struct { + cond: std.c.pthread_cond_t = .{}, + + fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { + if (builtin.mode == .Debug) { + mutex.impl.locking_thread.store(0, .unordered); + } + defer if (builtin.mode == .Debug) { + mutex.impl.locking_thread.store(std.Thread.getCurrentId(), .unordered); + }; + + const mtx = if (builtin.mode == .Debug) &mutex.impl.impl.mutex else &mutex.impl.mutex; + + if (timeout) |t| { + switch (std.c.pthread_cond_timedwait(&self.cond, mtx, &.{ + .sec = @intCast(@divFloor(t, std.time.ns_per_s)), + .nsec = @intCast(@mod(t, std.time.ns_per_s)), + })) { + .SUCCESS => return, + .TIMEDOUT => return error.Timeout, + else => unreachable, + } + } + + assert(std.c.pthread_cond_wait(&self.cond, mtx) == .SUCCESS); + } + + fn wake(self: *Impl, comptime notify: Notify) void { + assert(switch (notify) { + .one => std.c.pthread_cond_signal(&self.cond), + .all => std.c.pthread_cond_broadcast(&self.cond), + } == .SUCCESS); + } +}; + test "smoke test" { var mutex = Mutex{}; var cond = Condition{};