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