| ... | @@ -45,14 +45,30 @@ const Impl = if (builtin.mode == .Debug and !builtin.single_threaded) | ... | @@ -45,14 +45,30 @@ const Impl = if (builtin.mode == .Debug and !builtin.single_threaded) |
| 45 | else | 45 | else |
| 46 | ReleaseImpl; | 46 | ReleaseImpl; |
| 47 | | 47 | |
| 48 | const ReleaseImpl = if (builtin.single_threaded) | 48 | const ReleaseImpl = Impl: { |
| 49 | SingleThreadedImpl | 49 | if (builtin.single_threaded) break :Impl SingleThreadedImpl; |
| 50 | else if (builtin.os.tag == .windows) | 50 | if (builtin.os.tag == .windows) break :Impl WindowsImpl; |
| 51 | WindowsImpl | 51 | if (builtin.os.tag.isDarwin()) break :Impl DarwinImpl; |
| 52 | else if (builtin.os.tag.isDarwin()) | 52 | |
| 53 | DarwinImpl | 53 | if (builtin.target.os.tag == .linux or |
| 54 | else | 54 | builtin.target.os.tag == .freebsd or |
| 55 | FutexImpl; | 55 | builtin.target.os.tag == .openbsd or |
| | 56 | builtin.target.os.tag == .dragonfly or |
| | 57 | builtin.target.cpu.arch.isWasm()) |
| | 58 | { |
| | 59 | // Futex is the system's synchronization primitive; use that. |
| | 60 | break :Impl FutexImpl; |
| | 61 | } |
| | 62 | |
| | 63 | if (std.Thread.use_pthreads) { |
| | 64 | // This system doesn't have a futex primitive, so `std.Thread.Futex` is using `PosixImpl`, |
| | 65 | // which implements futex *on top of* pthread mutexes and conditions. Therefore, instead |
| | 66 | // of going through that long inefficient path, just use pthread mutex directly. |
| | 67 | break :Impl PosixImpl; |
| | 68 | } |
| | 69 | |
| | 70 | break :Impl FutexImpl; |
| | 71 | }; |
| 56 | | 72 | |
| 57 | const DebugImpl = struct { | 73 | const DebugImpl = struct { |
| 58 | locking_thread: std.atomic.Value(Thread.Id) = std.atomic.Value(Thread.Id).init(0), // 0 means it's not locked. | 74 | locking_thread: std.atomic.Value(Thread.Id) = std.atomic.Value(Thread.Id).init(0), // 0 means it's not locked. |
| ... | @@ -208,6 +224,37 @@ const FutexImpl = struct { | ... | @@ -208,6 +224,37 @@ const FutexImpl = struct { |
| 208 | } | 224 | } |
| 209 | }; | 225 | }; |
| 210 | | 226 | |
| | 227 | const PosixImpl = struct { |
| | 228 | mutex: std.c.pthread_mutex_t = .{}, |
| | 229 | |
| | 230 | fn tryLock(impl: *PosixImpl) bool { |
| | 231 | switch (std.c.pthread_mutex_trylock(&impl.mutex)) { |
| | 232 | .SUCCESS => return true, |
| | 233 | .BUSY => return false, |
| | 234 | .INVAL => unreachable, // mutex is initialized correctly |
| | 235 | else => unreachable, |
| | 236 | } |
| | 237 | } |
| | 238 | |
| | 239 | fn lock(impl: *PosixImpl) void { |
| | 240 | switch (std.c.pthread_mutex_lock(&impl.mutex)) { |
| | 241 | .SUCCESS => return, |
| | 242 | .INVAL => unreachable, // mutex is initialized correctly |
| | 243 | .DEADLK => unreachable, // not an error checking mutex |
| | 244 | else => unreachable, |
| | 245 | } |
| | 246 | } |
| | 247 | |
| | 248 | fn unlock(impl: *PosixImpl) void { |
| | 249 | switch (std.c.pthread_mutex_unlock(&impl.mutex)) { |
| | 250 | .SUCCESS => return, |
| | 251 | .INVAL => unreachable, // mutex is initialized correctly |
| | 252 | .PERM => unreachable, // not an error checking mutex |
| | 253 | else => unreachable, |
| | 254 | } |
| | 255 | } |
| | 256 | }; |
| | 257 | |
| 211 | test "smoke test" { | 258 | test "smoke test" { |
| 212 | var mutex = Mutex{}; | 259 | var mutex = Mutex{}; |
| 213 | | 260 | |