commit cf48041b55fbc2df0a2e12ae2a9490fc39e11486 (tree)
parent 5f950884a1390f135ea825b035b460ff680ebea3
Author: Alex Rønne Petersen <alex@alexrp.com>
Date: Sat, 24 Jan 2026 05:30:27 +0100
std.Thread.Condition: use pthread_cond_t impl when OS has no futex primitive
Same principle as #30835.
Diffstat:
1 file changed, 59 insertions(+), 6 deletions(-)
diff --git 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{};