| ... | ... | @@ -107,12 +107,30 @@ pub fn broadcast(self: *Condition) void { |
| 107 | 107 | self.impl.wake(.all); |
| 108 | 108 | } |
| 109 | 109 | |
| 110 | | const Impl = if (builtin.single_threaded) |
| 111 | | SingleThreadedImpl |
| 112 | | else if (builtin.os.tag == .windows) |
| 113 | | WindowsImpl |
| 114 | | else |
| 115 | | FutexImpl; |
| 110 | const 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 | }; |
| 116 | 134 | |
| 117 | 135 | const Notify = enum { |
| 118 | 136 | one, // wake up only one thread |
| ... | ... | @@ -291,6 +309,41 @@ const FutexImpl = struct { |
| 291 | 309 | } |
| 292 | 310 | }; |
| 293 | 311 | |
| 312 | const 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 | |
| 294 | 347 | test "smoke test" { |
| 295 | 348 | var mutex = Mutex{}; |
| 296 | 349 | var cond = Condition{}; |