| ... | @@ -5047,7 +5047,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat { | ... | @@ -5047,7 +5047,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat { |
| 5047 | }; | 5047 | }; |
| 5048 | } | 5048 | } |
| 5049 | | 5049 | |
| 5050 | fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat { | 5050 | fn statFromPosix(st: *const posix.Stat) Io.File.Stat { |
| 5051 | const atime = st.atime(); | 5051 | const atime = st.atime(); |
| 5052 | const mtime = st.mtime(); | 5052 | const mtime = st.mtime(); |
| 5053 | const ctime = st.ctime(); | 5053 | const ctime = st.ctime(); |
| ... | @@ -5056,20 +5056,20 @@ fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat { | ... | @@ -5056,20 +5056,20 @@ fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat { |
| 5056 | .size = @bitCast(st.size), | 5056 | .size = @bitCast(st.size), |
| 5057 | .mode = st.mode, | 5057 | .mode = st.mode, |
| 5058 | .kind = k: { | 5058 | .kind = k: { |
| 5059 | const m = st.mode & std.posix.S.IFMT; | 5059 | const m = st.mode & posix.S.IFMT; |
| 5060 | switch (m) { | 5060 | switch (m) { |
| 5061 | std.posix.S.IFBLK => break :k .block_device, | 5061 | posix.S.IFBLK => break :k .block_device, |
| 5062 | std.posix.S.IFCHR => break :k .character_device, | 5062 | posix.S.IFCHR => break :k .character_device, |
| 5063 | std.posix.S.IFDIR => break :k .directory, | 5063 | posix.S.IFDIR => break :k .directory, |
| 5064 | std.posix.S.IFIFO => break :k .named_pipe, | 5064 | posix.S.IFIFO => break :k .named_pipe, |
| 5065 | std.posix.S.IFLNK => break :k .sym_link, | 5065 | posix.S.IFLNK => break :k .sym_link, |
| 5066 | std.posix.S.IFREG => break :k .file, | 5066 | posix.S.IFREG => break :k .file, |
| 5067 | std.posix.S.IFSOCK => break :k .unix_domain_socket, | 5067 | posix.S.IFSOCK => break :k .unix_domain_socket, |
| 5068 | else => {}, | 5068 | else => {}, |
| 5069 | } | 5069 | } |
| 5070 | if (native_os == .illumos) switch (m) { | 5070 | if (native_os == .illumos) switch (m) { |
| 5071 | std.posix.S.IFDOOR => break :k .door, | 5071 | posix.S.IFDOOR => break :k .door, |
| 5072 | std.posix.S.IFPORT => break :k .event_port, | 5072 | posix.S.IFPORT => break :k .event_port, |
| 5073 | else => {}, | 5073 | else => {}, |
| 5074 | }; | 5074 | }; |
| 5075 | | 5075 | |
| ... | @@ -5101,11 +5101,11 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat { | ... | @@ -5101,11 +5101,11 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat { |
| 5101 | }; | 5101 | }; |
| 5102 | } | 5102 | } |
| 5103 | | 5103 | |
| 5104 | fn timestampFromPosix(timespec: *const std.posix.timespec) Io.Timestamp { | 5104 | fn timestampFromPosix(timespec: *const posix.timespec) Io.Timestamp { |
| 5105 | return .{ .nanoseconds = @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec) }; | 5105 | return .{ .nanoseconds = @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec) }; |
| 5106 | } | 5106 | } |
| 5107 | | 5107 | |
| 5108 | fn timestampToPosix(nanoseconds: i96) std.posix.timespec { | 5108 | fn timestampToPosix(nanoseconds: i96) posix.timespec { |
| 5109 | return .{ | 5109 | return .{ |
| 5110 | .sec = @intCast(@divFloor(nanoseconds, std.time.ns_per_s)), | 5110 | .sec = @intCast(@divFloor(nanoseconds, std.time.ns_per_s)), |
| 5111 | .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)), | 5111 | .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)), |
| ... | @@ -5503,44 +5503,7 @@ const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= | ... | @@ -5503,44 +5503,7 @@ const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= |
| 5503 | fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { | 5503 | fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { |
| 5504 | @branchHint(.cold); | 5504 | @branchHint(.cold); |
| 5505 | | 5505 | |
| 5506 | if (native_os == .linux) { | 5506 | if (builtin.cpu.arch.isWasm()) { |
| 5507 | const linux = std.os.linux; | | |
| 5508 | try t.checkCancel(); | | |
| 5509 | const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); | | |
| 5510 | if (is_debug) switch (linux.E.init(rc)) { | | |
| 5511 | .SUCCESS => {}, // notified by `wake()` | | |
| 5512 | .INTR => {}, // gives caller a chance to check cancellation | | |
| 5513 | .AGAIN => {}, // ptr.* != expect | | |
| 5514 | .INVAL => {}, // possibly timeout overflow | | |
| 5515 | .TIMEDOUT => unreachable, | | |
| 5516 | .FAULT => unreachable, // ptr was invalid | | |
| 5517 | else => unreachable, | | |
| 5518 | }; | | |
| 5519 | } else if (native_os.isDarwin()) { | | |
| 5520 | const c = std.c; | | |
| 5521 | const flags: c.UL = .{ | | |
| 5522 | .op = .COMPARE_AND_WAIT, | | |
| 5523 | .NO_ERRNO = true, | | |
| 5524 | }; | | |
| 5525 | try t.checkCancel(); | | |
| 5526 | const status = if (darwin_supports_ulock_wait2) | | |
| 5527 | c.__ulock_wait2(flags, ptr, expect, 0, 0) | | |
| 5528 | else | | |
| 5529 | c.__ulock_wait(flags, ptr, expect, 0); | | |
| 5530 | | | |
| 5531 | if (status >= 0) return; | | |
| 5532 | | | |
| 5533 | if (is_debug) switch (@as(c.E, @enumFromInt(-status))) { | | |
| 5534 | // Wait was interrupted by the OS or other spurious signalling. | | |
| 5535 | .INTR => {}, | | |
| 5536 | // Address of the futex was paged out. This is unlikely, but possible in theory, and | | |
| 5537 | // pthread/libdispatch on darwin bother to handle it. In this case we'll return | | |
| 5538 | // without waiting, but the caller should retry anyway. | | |
| 5539 | .FAULT => {}, | | |
| 5540 | .TIMEDOUT => unreachable, | | |
| 5541 | else => unreachable, | | |
| 5542 | }; | | |
| 5543 | } else if (builtin.cpu.arch.isWasm()) { | | |
| 5544 | comptime assert(builtin.cpu.has(.wasm, .atomics)); | 5507 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| 5545 | try t.checkCancel(); | 5508 | try t.checkCancel(); |
| 5546 | const timeout: i64 = -1; | 5509 | const timeout: i64 = -1; |
| ... | @@ -5562,57 +5525,74 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca | ... | @@ -5562,57 +5525,74 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca |
| 5562 | 2 => assert(!is_debug), // timeout | 5525 | 2 => assert(!is_debug), // timeout |
| 5563 | else => assert(!is_debug), | 5526 | else => assert(!is_debug), |
| 5564 | } | 5527 | } |
| 5565 | } else if (is_windows) { | 5528 | } else switch (native_os) { |
| 5566 | try t.checkCancel(); | 5529 | .linux => { |
| 5567 | switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), null)) { | 5530 | const linux = std.os.linux; |
| 5568 | .SUCCESS => {}, | 5531 | try t.checkCancel(); |
| 5569 | .CANCELLED => return error.Canceled, | 5532 | const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); |
| 5570 | else => recoverableOsBugDetected(), | 5533 | if (is_debug) switch (linux.E.init(rc)) { |
| 5571 | } | 5534 | .SUCCESS => {}, // notified by `wake()` |
| 5572 | } else { | 5535 | .INTR => {}, // gives caller a chance to check cancellation |
| 5573 | @compileError("TODO"); | 5536 | .AGAIN => {}, // ptr.* != expect |
| | 5537 | .INVAL => {}, // possibly timeout overflow |
| | 5538 | .TIMEDOUT => unreachable, |
| | 5539 | .FAULT => unreachable, // ptr was invalid |
| | 5540 | else => unreachable, |
| | 5541 | }; |
| | 5542 | }, |
| | 5543 | .driverkit, .ios, .macos, .tvos, .visionos, .watchos => { |
| | 5544 | const c = std.c; |
| | 5545 | const flags: c.UL = .{ |
| | 5546 | .op = .COMPARE_AND_WAIT, |
| | 5547 | .NO_ERRNO = true, |
| | 5548 | }; |
| | 5549 | try t.checkCancel(); |
| | 5550 | const status = if (darwin_supports_ulock_wait2) |
| | 5551 | c.__ulock_wait2(flags, ptr, expect, 0, 0) |
| | 5552 | else |
| | 5553 | c.__ulock_wait(flags, ptr, expect, 0); |
| | 5554 | |
| | 5555 | if (status >= 0) return; |
| | 5556 | |
| | 5557 | if (is_debug) switch (@as(c.E, @enumFromInt(-status))) { |
| | 5558 | .INTR => {}, // spurious wake |
| | 5559 | // Address of the futex was paged out. This is unlikely, but possible in theory, and |
| | 5560 | // pthread/libdispatch on darwin bother to handle it. In this case we'll return |
| | 5561 | // without waiting, but the caller should retry anyway. |
| | 5562 | .FAULT => {}, |
| | 5563 | .TIMEDOUT => unreachable, |
| | 5564 | else => unreachable, |
| | 5565 | }; |
| | 5566 | }, |
| | 5567 | .windows => { |
| | 5568 | try t.checkCancel(); |
| | 5569 | switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), null)) { |
| | 5570 | .SUCCESS => {}, |
| | 5571 | .CANCELLED => return error.Canceled, |
| | 5572 | else => recoverableOsBugDetected(), |
| | 5573 | } |
| | 5574 | }, |
| | 5575 | .freebsd => { |
| | 5576 | const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE); |
| | 5577 | try t.checkCancel(); |
| | 5578 | const rc = std.c._umtx_op(@intFromPtr(&ptr.raw), flags, @as(c_ulong, expect), 0, 0); |
| | 5579 | if (is_debug) switch (posix.errno(rc)) { |
| | 5580 | .SUCCESS => {}, |
| | 5581 | .FAULT => unreachable, // one of the args points to invalid memory |
| | 5582 | .INVAL => unreachable, // arguments should be correct |
| | 5583 | .TIMEDOUT => unreachable, // no timeout provided |
| | 5584 | .INTR => {}, // spurious wake |
| | 5585 | else => unreachable, |
| | 5586 | }; |
| | 5587 | }, |
| | 5588 | else => @compileError("unimplemented: futexWait"), |
| 5574 | } | 5589 | } |
| 5575 | } | 5590 | } |
| 5576 | | 5591 | |
| 5577 | pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) void { | 5592 | pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) void { |
| 5578 | @branchHint(.cold); | 5593 | @branchHint(.cold); |
| 5579 | | 5594 | |
| 5580 | if (native_os == .linux) { | 5595 | if (builtin.cpu.arch.isWasm()) { |
| 5581 | const linux = std.os.linux; | | |
| 5582 | const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); | | |
| 5583 | if (is_debug) switch (linux.E.init(rc)) { | | |
| 5584 | .SUCCESS => {}, // notified by `wake()` | | |
| 5585 | .INTR => {}, // gives caller a chance to check cancellation | | |
| 5586 | .AGAIN => {}, // ptr.* != expect | | |
| 5587 | .INVAL => {}, // possibly timeout overflow | | |
| 5588 | .TIMEDOUT => unreachable, | | |
| 5589 | .FAULT => unreachable, // ptr was invalid | | |
| 5590 | else => unreachable, | | |
| 5591 | }; | | |
| 5592 | } else if (native_os.isDarwin()) { | | |
| 5593 | const c = std.c; | | |
| 5594 | const flags: c.UL = .{ | | |
| 5595 | .op = .COMPARE_AND_WAIT, | | |
| 5596 | .NO_ERRNO = true, | | |
| 5597 | }; | | |
| 5598 | const status = if (darwin_supports_ulock_wait2) | | |
| 5599 | c.__ulock_wait2(flags, ptr, expect, 0, 0) | | |
| 5600 | else | | |
| 5601 | c.__ulock_wait(flags, ptr, expect, 0); | | |
| 5602 | | | |
| 5603 | if (status >= 0) return; | | |
| 5604 | | | |
| 5605 | if (is_debug) switch (@as(c.E, @enumFromInt(-status))) { | | |
| 5606 | // Wait was interrupted by the OS or other spurious signalling. | | |
| 5607 | .INTR => {}, | | |
| 5608 | // Address of the futex was paged out. This is unlikely, but possible in theory, and | | |
| 5609 | // pthread/libdispatch on darwin bother to handle it. In this case we'll return | | |
| 5610 | // without waiting, but the caller should retry anyway. | | |
| 5611 | .FAULT => {}, | | |
| 5612 | .TIMEDOUT => unreachable, | | |
| 5613 | else => unreachable, | | |
| 5614 | }; | | |
| 5615 | } else if (builtin.cpu.arch.isWasm()) { | | |
| 5616 | comptime assert(builtin.cpu.has(.wasm, .atomics)); | 5596 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| 5617 | const timeout: i64 = -1; | 5597 | const timeout: i64 = -1; |
| 5618 | const signed_expect: i32 = @bitCast(expect); | 5598 | const signed_expect: i32 = @bitCast(expect); |
| ... | @@ -5630,16 +5610,66 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi | ... | @@ -5630,16 +5610,66 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi |
| 5630 | switch (result) { | 5610 | switch (result) { |
| 5631 | 0 => {}, // ok | 5611 | 0 => {}, // ok |
| 5632 | 1 => {}, // expected != loaded | 5612 | 1 => {}, // expected != loaded |
| 5633 | 2 => assert(!is_debug), // timeout | 5613 | 2 => recoverableOsBugDetected(), // timeout |
| 5634 | else => assert(!is_debug), | | |
| 5635 | } | | |
| 5636 | } else if (is_windows) { | | |
| 5637 | switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), null)) { | | |
| 5638 | .SUCCESS, .CANCELLED => {}, | | |
| 5639 | else => recoverableOsBugDetected(), | 5614 | else => recoverableOsBugDetected(), |
| 5640 | } | 5615 | } |
| 5641 | } else { | 5616 | } else switch (native_os) { |
| 5642 | @compileError("TODO"); | 5617 | .linux => { |
| | 5618 | const linux = std.os.linux; |
| | 5619 | const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); |
| | 5620 | switch (linux.E.init(rc)) { |
| | 5621 | .SUCCESS => {}, // notified by `wake()` |
| | 5622 | .INTR => {}, // gives caller a chance to check cancellation |
| | 5623 | .AGAIN => {}, // ptr.* != expect |
| | 5624 | .INVAL => {}, // possibly timeout overflow |
| | 5625 | .TIMEDOUT => recoverableOsBugDetected(), |
| | 5626 | .FAULT => recoverableOsBugDetected(), // ptr was invalid |
| | 5627 | else => recoverableOsBugDetected(), |
| | 5628 | } |
| | 5629 | }, |
| | 5630 | .driverkit, .ios, .macos, .tvos, .visionos, .watchos => { |
| | 5631 | const c = std.c; |
| | 5632 | const flags: c.UL = .{ |
| | 5633 | .op = .COMPARE_AND_WAIT, |
| | 5634 | .NO_ERRNO = true, |
| | 5635 | }; |
| | 5636 | const status = if (darwin_supports_ulock_wait2) |
| | 5637 | c.__ulock_wait2(flags, ptr, expect, 0, 0) |
| | 5638 | else |
| | 5639 | c.__ulock_wait(flags, ptr, expect, 0); |
| | 5640 | |
| | 5641 | if (status >= 0) return; |
| | 5642 | |
| | 5643 | switch (@as(c.E, @enumFromInt(-status))) { |
| | 5644 | // Wait was interrupted by the OS or other spurious signalling. |
| | 5645 | .INTR => {}, |
| | 5646 | // Address of the futex was paged out. This is unlikely, but possible in theory, and |
| | 5647 | // pthread/libdispatch on darwin bother to handle it. In this case we'll return |
| | 5648 | // without waiting, but the caller should retry anyway. |
| | 5649 | .FAULT => {}, |
| | 5650 | .TIMEDOUT => recoverableOsBugDetected(), |
| | 5651 | else => recoverableOsBugDetected(), |
| | 5652 | } |
| | 5653 | }, |
| | 5654 | .windows => { |
| | 5655 | switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), null)) { |
| | 5656 | .SUCCESS, .CANCELLED => {}, |
| | 5657 | else => recoverableOsBugDetected(), |
| | 5658 | } |
| | 5659 | }, |
| | 5660 | .freebsd => { |
| | 5661 | const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE); |
| | 5662 | const rc = std.c._umtx_op(@intFromPtr(&ptr.raw), flags, @as(c_ulong, expect), 0, 0); |
| | 5663 | switch (posix.errno(rc)) { |
| | 5664 | .SUCCESS => {}, |
| | 5665 | .INTR => {}, // spurious wake |
| | 5666 | .FAULT => recoverableOsBugDetected(), // one of the args points to invalid memory |
| | 5667 | .INVAL => recoverableOsBugDetected(), // arguments should be correct |
| | 5668 | .TIMEDOUT => recoverableOsBugDetected(), // no timeout provided |
| | 5669 | else => recoverableOsBugDetected(), |
| | 5670 | } |
| | 5671 | }, |
| | 5672 | else => @compileError("unimplemented: futexWaitUncancelable"), |
| 5643 | } | 5673 | } |
| 5644 | } | 5674 | } |
| 5645 | | 5675 | |
| ... | @@ -5668,38 +5698,7 @@ pub fn futexWaitDurationUncancelable(ptr: *const std.atomic.Value(u32), expect: | ... | @@ -5668,38 +5698,7 @@ pub fn futexWaitDurationUncancelable(ptr: *const std.atomic.Value(u32), expect: |
| 5668 | pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { | 5698 | pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { |
| 5669 | @branchHint(.cold); | 5699 | @branchHint(.cold); |
| 5670 | | 5700 | |
| 5671 | if (native_os == .linux) { | 5701 | if (builtin.cpu.arch.isWasm()) { |
| 5672 | const linux = std.os.linux; | | |
| 5673 | const rc = linux.futex_3arg( | | |
| 5674 | &ptr.raw, | | |
| 5675 | .{ .cmd = .WAKE, .private = true }, | | |
| 5676 | @min(max_waiters, std.math.maxInt(i32)), | | |
| 5677 | ); | | |
| 5678 | if (is_debug) switch (linux.E.init(rc)) { | | |
| 5679 | .SUCCESS => {}, // successful wake up | | |
| 5680 | .INVAL => {}, // invalid futex_wait() on ptr done elsewhere | | |
| 5681 | .FAULT => {}, // pointer became invalid while doing the wake | | |
| 5682 | else => unreachable, | | |
| 5683 | }; | | |
| 5684 | } else if (native_os.isDarwin()) { | | |
| 5685 | const c = std.c; | | |
| 5686 | const flags: c.UL = .{ | | |
| 5687 | .op = .COMPARE_AND_WAIT, | | |
| 5688 | .NO_ERRNO = true, | | |
| 5689 | .WAKE_ALL = max_waiters > 1, | | |
| 5690 | }; | | |
| 5691 | while (true) { | | |
| 5692 | const status = c.__ulock_wake(flags, ptr, 0); | | |
| 5693 | if (status >= 0) return; | | |
| 5694 | switch (@as(c.E, @enumFromInt(-status))) { | | |
| 5695 | .INTR, .CANCELED => continue, // spurious wake() | | |
| 5696 | .FAULT => assert(!is_debug), // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t | | |
| 5697 | .NOENT => return, // nothing was woken up | | |
| 5698 | .ALREADY => assert(!is_debug), // only for UL.Op.WAKE_THREAD | | |
| 5699 | else => assert(!is_debug), | | |
| 5700 | } | | |
| 5701 | } | | |
| 5702 | } else if (builtin.cpu.arch.isWasm()) { | | |
| 5703 | comptime assert(builtin.cpu.has(.wasm, .atomics)); | 5702 | comptime assert(builtin.cpu.has(.wasm, .atomics)); |
| 5704 | assert(max_waiters != 0); | 5703 | assert(max_waiters != 0); |
| 5705 | const woken_count = asm volatile ( | 5704 | const woken_count = asm volatile ( |
| ... | @@ -5712,14 +5711,63 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { | ... | @@ -5712,14 +5711,63 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { |
| 5712 | [waiters] "r" (max_waiters), | 5711 | [waiters] "r" (max_waiters), |
| 5713 | ); | 5712 | ); |
| 5714 | _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled | 5713 | _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled |
| 5715 | } else if (is_windows) { | 5714 | } else switch (native_os) { |
| 5716 | assert(max_waiters != 0); | 5715 | .linux => { |
| 5717 | switch (max_waiters) { | 5716 | const linux = std.os.linux; |
| 5718 | 1 => windows.ntdll.RtlWakeAddressSingle(ptr), | 5717 | const rc = linux.futex_3arg( |
| 5719 | else => windows.ntdll.RtlWakeAddressAll(ptr), | 5718 | &ptr.raw, |
| 5720 | } | 5719 | .{ .cmd = .WAKE, .private = true }, |
| 5721 | } else { | 5720 | @min(max_waiters, std.math.maxInt(i32)), |
| 5722 | @compileError("TODO"); | 5721 | ); |
| | 5722 | if (is_debug) switch (linux.E.init(rc)) { |
| | 5723 | .SUCCESS => {}, // successful wake up |
| | 5724 | .INVAL => {}, // invalid futex_wait() on ptr done elsewhere |
| | 5725 | .FAULT => {}, // pointer became invalid while doing the wake |
| | 5726 | else => unreachable, // deadlock due to operating system bug |
| | 5727 | }; |
| | 5728 | }, |
| | 5729 | .driverkit, .ios, .macos, .tvos, .visionos, .watchos => { |
| | 5730 | const c = std.c; |
| | 5731 | const flags: c.UL = .{ |
| | 5732 | .op = .COMPARE_AND_WAIT, |
| | 5733 | .NO_ERRNO = true, |
| | 5734 | .WAKE_ALL = max_waiters > 1, |
| | 5735 | }; |
| | 5736 | while (true) { |
| | 5737 | const status = c.__ulock_wake(flags, ptr, 0); |
| | 5738 | if (status >= 0) return; |
| | 5739 | switch (@as(c.E, @enumFromInt(-status))) { |
| | 5740 | .INTR, .CANCELED => continue, // spurious wake() |
| | 5741 | .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t |
| | 5742 | .NOENT => return, // nothing was woken up |
| | 5743 | .ALREADY => unreachable, // only for UL.Op.WAKE_THREAD |
| | 5744 | else => unreachable, // deadlock due to operating system bug |
| | 5745 | } |
| | 5746 | } |
| | 5747 | }, |
| | 5748 | .windows => { |
| | 5749 | assert(max_waiters != 0); |
| | 5750 | switch (max_waiters) { |
| | 5751 | 1 => windows.ntdll.RtlWakeAddressSingle(ptr), |
| | 5752 | else => windows.ntdll.RtlWakeAddressAll(ptr), |
| | 5753 | } |
| | 5754 | }, |
| | 5755 | .freebsd => { |
| | 5756 | const rc = std.c._umtx_op( |
| | 5757 | @intFromPtr(&ptr.raw), |
| | 5758 | @intFromEnum(std.c.UMTX_OP.WAKE_PRIVATE), |
| | 5759 | @as(c_ulong, max_waiters), |
| | 5760 | 0, // there is no timeout struct |
| | 5761 | 0, // there is no timeout struct pointer |
| | 5762 | ); |
| | 5763 | switch (posix.errno(rc)) { |
| | 5764 | .SUCCESS => {}, |
| | 5765 | .FAULT => {}, // it's ok if the ptr doesn't point to valid memory |
| | 5766 | .INVAL => unreachable, // arguments should be correct |
| | 5767 | else => unreachable, // deadlock due to operating system bug |
| | 5768 | } |
| | 5769 | }, |
| | 5770 | else => @compileError("unimplemented: futexWake"), |
| 5723 | } | 5771 | } |
| 5724 | } | 5772 | } |
| 5725 | | 5773 | |