authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-01-24 05:30:27+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-01-24 16:44:55+01:00
logcf48041b55fbc2df0a2e12ae2a9490fc39e11486
treee0fd168752f3d2c053a8eb3f77023f2e125e684c
parent5f950884a1390f135ea825b035b460ff680ebea3

std.Thread.Condition: use pthread_cond_t impl when OS has no futex primitive

Same principle as #30835.

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

lib/std/Thread/Condition.zig+59-6
......@@ -107,12 +107,30 @@ pub fn broadcast(self: *Condition) void {
107107 self.impl.wake(.all);
108108}
109109
110const Impl = if (builtin.single_threaded)
111 SingleThreadedImpl
112else if (builtin.os.tag == .windows)
113 WindowsImpl
114else
115 FutexImpl;
110const Impl = Impl: {
111 if (builtin.single_threaded) break :Impl SingleThreadedImpl;
112 if (builtin.os.tag == .windows) break :Impl WindowsImpl;
113
114 if (builtin.os.tag.isDarwin() or
115 builtin.target.os.tag == .linux or
116 builtin.target.os.tag == .freebsd or
117 builtin.target.os.tag == .openbsd or
118 builtin.target.os.tag == .dragonfly or
119 builtin.target.cpu.arch.isWasm())
120 {
121 // Futex is the system's synchronization primitive; use that.
122 break :Impl FutexImpl;
123 }
124
125 if (std.Thread.use_pthreads) {
126 // This system doesn't have a futex primitive, so `std.Thread.Futex` is using `PosixImpl`,
127 // which implements futex *on top of* pthread mutexes and conditions. Therefore, instead
128 // of going through that long inefficient path, just use pthread condition variable directly.
129 break :Impl PosixImpl;
130 }
131
132 break :Impl FutexImpl;
133};
116134
117135const Notify = enum {
118136 one, // wake up only one thread
......@@ -291,6 +309,41 @@ const FutexImpl = struct {
291309 }
292310};
293311
312const PosixImpl = struct {
313 cond: std.c.pthread_cond_t = .{},
314
315 fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void {
316 if (builtin.mode == .Debug) {
317 mutex.impl.locking_thread.store(0, .unordered);
318 }
319 defer if (builtin.mode == .Debug) {
320 mutex.impl.locking_thread.store(std.Thread.getCurrentId(), .unordered);
321 };
322
323 const mtx = if (builtin.mode == .Debug) &mutex.impl.impl.mutex else &mutex.impl.mutex;
324
325 if (timeout) |t| {
326 switch (std.c.pthread_cond_timedwait(&self.cond, mtx, &.{
327 .sec = @intCast(@divFloor(t, std.time.ns_per_s)),
328 .nsec = @intCast(@mod(t, std.time.ns_per_s)),
329 })) {
330 .SUCCESS => return,
331 .TIMEDOUT => return error.Timeout,
332 else => unreachable,
333 }
334 }
335
336 assert(std.c.pthread_cond_wait(&self.cond, mtx) == .SUCCESS);
337 }
338
339 fn wake(self: *Impl, comptime notify: Notify) void {
340 assert(switch (notify) {
341 .one => std.c.pthread_cond_signal(&self.cond),
342 .all => std.c.pthread_cond_broadcast(&self.cond),
343 } == .SUCCESS);
344 }
345};
346
294347test "smoke test" {
295348 var mutex = Mutex{};
296349 var cond = Condition{};