| ... | @@ -3963,10 +3963,7 @@ pub fn dirOpenFileWtf16( | ... | @@ -3963,10 +3963,7 @@ pub fn dirOpenFileWtf16( |
| 3963 | // kernel bug with retry attempts. | 3963 | // kernel bug with retry attempts. |
| 3964 | syscall.finish(); | 3964 | syscall.finish(); |
| 3965 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3965 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3966 | try parking_sleep.sleep(.{ .duration = .{ | 3966 | try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1); |
| 3967 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | | |
| 3968 | .clock = .awake, | | |
| 3969 | } }); | | |
| 3970 | attempt += 1; | 3967 | attempt += 1; |
| 3971 | syscall = try .start(); | 3968 | syscall = try .start(); |
| 3972 | continue; | 3969 | continue; |
| ... | @@ -3988,10 +3985,7 @@ pub fn dirOpenFileWtf16( | ... | @@ -3988,10 +3985,7 @@ pub fn dirOpenFileWtf16( |
| 3988 | // fixed by sleeping and retrying until the error goes away. | 3985 | // fixed by sleeping and retrying until the error goes away. |
| 3989 | syscall.finish(); | 3986 | syscall.finish(); |
| 3990 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3987 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3991 | try parking_sleep.sleep(.{ .duration = .{ | 3988 | try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1); |
| 3992 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | | |
| 3993 | .clock = .awake, | | |
| 3994 | } }); | | |
| 3995 | attempt += 1; | 3989 | attempt += 1; |
| 3996 | syscall = try .start(); | 3990 | syscall = try .start(); |
| 3997 | continue; | 3991 | continue; |
| ... | @@ -9638,6 +9632,9 @@ fn nowPosix(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | ... | @@ -9638,6 +9632,9 @@ fn nowPosix(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 9638 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | 9632 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 9639 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 9633 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 9640 | _ = t; | 9634 | _ = t; |
| | 9635 | return nowInner(clock); |
| | 9636 | } |
| | 9637 | fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 9641 | return switch (native_os) { | 9638 | return switch (native_os) { |
| 9642 | .windows => nowWindows(clock), | 9639 | .windows => nowWindows(clock), |
| 9643 | .wasi => nowWasi(clock), | 9640 | .wasi => nowWasi(clock), |
| ... | @@ -9687,7 +9684,7 @@ fn nowWasi(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | ... | @@ -9687,7 +9684,7 @@ fn nowWasi(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 9687 | | 9684 | |
| 9688 | fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { | 9685 | fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { |
| 9689 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 9686 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 9690 | if (use_parking_sleep) return parking_sleep.sleep(timeout); | 9687 | if (use_parking_sleep) return parking_sleep.sleep(try timeout.toDeadline(ioBasic(t))); |
| 9691 | if (native_os == .wasi) return sleepWasi(t, timeout); | 9688 | if (native_os == .wasi) return sleepWasi(t, timeout); |
| 9692 | if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout); | 9689 | if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout); |
| 9693 | return sleepNanosleep(t, timeout); | 9690 | return sleepNanosleep(t, timeout); |
| ... | @@ -14197,10 +14194,7 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -14197,10 +14194,7 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 14197 | // this other than retrying the creation after the OS finishes | 14194 | // this other than retrying the creation after the OS finishes |
| 14198 | // the deletion. | 14195 | // the deletion. |
| 14199 | syscall.finish(); | 14196 | syscall.finish(); |
| 14200 | try parking_sleep.sleep(.{ .duration = .{ | 14197 | try parking_sleep.windowsRetrySleep(1); |
| 14201 | .raw = .fromMilliseconds(1), | | |
| 14202 | .clock = .awake, | | |
| 14203 | } }); | | |
| 14204 | syscall = try .start(); | 14198 | syscall = try .start(); |
| 14205 | continue; | 14199 | continue; |
| 14206 | }, | 14200 | }, |
| ... | @@ -15570,9 +15564,13 @@ const parking_futex = struct { | ... | @@ -15570,9 +15564,13 @@ const parking_futex = struct { |
| 15570 | /// | 15564 | /// |
| 15571 | /// * Removing the `Waiter` from `Bucket.waiters` | 15565 | /// * Removing the `Waiter` from `Bucket.waiters` |
| 15572 | /// * Decrementing `Bucket.num_waiters` | 15566 | /// * Decrementing `Bucket.num_waiters` |
| 15573 | /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope | 15567 | /// * Atomically setting `done` (after this, the `Waiter` may go out of scope at any time, |
| 15574 | /// while it is still in the `Bucket`). | 15568 | /// so must not be referenced again) |
| | 15569 | /// * Unparking the thread (last, so that the unparked thread definitely sees `done`) |
| 15575 | thread_status: *std.atomic.Value(Thread.Status), | 15570 | thread_status: *std.atomic.Value(Thread.Status), |
| | 15571 | /// Initially `false`. Whoever updates `thread_status` to `.none`/`.canceling` will update |
| | 15572 | /// this to `true` once they are done with the `Waiter`, just before unparking `tid`. |
| | 15573 | done: std.atomic.Value(bool), |
| 15576 | }; | 15574 | }; |
| 15577 | | 15575 | |
| 15578 | fn bucketForAddress(address: usize) *Bucket { | 15576 | fn bucketForAddress(address: usize) *Bucket { |
| ... | @@ -15611,6 +15609,7 @@ const parking_futex = struct { | ... | @@ -15611,6 +15609,7 @@ const parking_futex = struct { |
| 15611 | .address = @intFromPtr(ptr), | 15609 | .address = @intFromPtr(ptr), |
| 15612 | .tid = self_tid, | 15610 | .tid = self_tid, |
| 15613 | .thread_status = undefined, // populated in critical section | 15611 | .thread_status = undefined, // populated in critical section |
| | 15612 | .done = .init(false), |
| 15614 | }; | 15613 | }; |
| 15615 | | 15614 | |
| 15616 | var status_buf: std.atomic.Value(Thread.Status) = undefined; | 15615 | var status_buf: std.atomic.Value(Thread.Status) = undefined; |
| ... | @@ -15667,40 +15666,43 @@ const parking_futex = struct { | ... | @@ -15667,40 +15666,43 @@ const parking_futex = struct { |
| 15667 | bucket.waiters.append(&waiter.node); | 15666 | bucket.waiters.append(&waiter.node); |
| 15668 | } | 15667 | } |
| 15669 | | 15668 | |
| 15670 | if (park(timeout, ptr)) { | 15669 | const deadline: ?Io.Clock.Timestamp = switch (timeout) { |
| 15671 | // We were unparked by either `wake` or cancelation, so our current status is either | 15670 | .none => null, |
| 15672 | // `.none` or `.canceling`. In either case, they've already removed `waiter` from | 15671 | .duration => |d| .{ |
| 15673 | // `bucket`, so we have nothing more to do! | 15672 | .raw = (nowInner(d.clock) catch unreachable).addDuration(d.raw), |
| | 15673 | .clock = d.clock, |
| | 15674 | }, |
| | 15675 | .deadline => |d| d, |
| | 15676 | }; |
| | 15677 | while (park(deadline, ptr)) { |
| | 15678 | if (waiter.done.load(.acquire)) return; // all done! |
| 15674 | } else |err| switch (err) { | 15679 | } else |err| switch (err) { |
| 15675 | error.Timeout => { | 15680 | error.Timeout => switch (waiter.thread_status.fetchAnd( |
| 15676 | // We're not out of the woods yet: an unpark could race with the timeout. | 15681 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, |
| 15677 | const old_status = waiter.thread_status.fetchAnd( | 15682 | .monotonic, |
| 15678 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, | 15683 | ).cancelation) { |
| 15679 | .monotonic, | 15684 | .parked => { |
| 15680 | ); | 15685 | // We saw a timeout and updated our own status from `.parked` to `.none`. It is |
| 15681 | switch (old_status.cancelation) { | 15686 | // our responsibility to remove `waiter` from `bucket`. |
| 15682 | .parked => { | 15687 | bucket.mutex.lock(); |
| 15683 | // No race. It is our responsibility to remove `waiter` from `bucket`. | 15688 | defer bucket.mutex.unlock(); |
| 15684 | // New status is `.none`. | 15689 | bucket.waiters.remove(&waiter.node); |
| 15685 | bucket.mutex.lock(); | 15690 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 15686 | defer bucket.mutex.unlock(); | 15691 | }, |
| 15687 | bucket.waiters.remove(&waiter.node); | 15692 | .none, .canceling => { |
| 15688 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | 15693 | // Race condition: the timeout was reached, then `wake` or a cancelation tried |
| 15689 | }, | 15694 | // to update our status. They won the race, so wait for them to do the cleanup. |
| 15690 | .none, .canceling => { | 15695 | // They'll tell us by setting `waiter.done` and unparking us. |
| 15691 | // Race condition: the timeout was reached, then `wake` or a canceler tried | 15696 | while (!waiter.done.load(.acquire)) { |
| 15692 | // to unpark us. Whoever did that will remove us from `bucket`. Wait for | 15697 | park(null, ptr) catch |e| switch (e) { |
| 15693 | // that (and drop the unpark request in doing so). | | |
| 15694 | // New status is `.none` or `.canceling` respectively. | | |
| 15695 | park(.none, ptr) catch |e| switch (e) { | | |
| 15696 | error.Timeout => unreachable, | 15698 | error.Timeout => unreachable, |
| 15697 | }; | 15699 | }; |
| 15698 | }, | 15700 | } |
| 15699 | .canceled => unreachable, | 15701 | }, |
| 15700 | .blocked => unreachable, | 15702 | .canceled => unreachable, |
| 15701 | .blocked_windows_dns => unreachable, | 15703 | .blocked => unreachable, |
| 15702 | .blocked_canceling => unreachable, | 15704 | .blocked_windows_dns => unreachable, |
| 15703 | } | 15705 | .blocked_canceling => unreachable, |
| 15704 | }, | 15706 | }, |
| 15705 | } | 15707 | } |
| 15706 | } | 15708 | } |
| ... | @@ -15748,9 +15750,6 @@ const parking_futex = struct { | ... | @@ -15748,9 +15750,6 @@ const parking_futex = struct { |
| 15748 | waiter.node.next = waking_head; | 15750 | waiter.node.next = waking_head; |
| 15749 | waking_head = &waiter.node; | 15751 | waking_head = &waiter.node; |
| 15750 | num_removed += 1; | 15752 | num_removed += 1; |
| 15751 | // Signal to `waiter` that they're about to be unparked, in case we're racing with their | | |
| 15752 | // timeout. See corresponding logic in `wake`. | | |
| 15753 | waiter.address = 0; | | |
| 15754 | } | 15753 | } |
| 15755 | | 15754 | |
| 15756 | _ = bucket.num_waiters.fetchSub(num_removed, .monotonic); | 15755 | _ = bucket.num_waiters.fetchSub(num_removed, .monotonic); |
| ... | @@ -15765,6 +15764,8 @@ const parking_futex = struct { | ... | @@ -15765,6 +15764,8 @@ const parking_futex = struct { |
| 15765 | const waiter: *Waiter = @fieldParentPtr("node", node); | 15764 | const waiter: *Waiter = @fieldParentPtr("node", node); |
| 15766 | unpark_buf[unpark_len] = waiter.tid; | 15765 | unpark_buf[unpark_len] = waiter.tid; |
| 15767 | unpark_len += 1; | 15766 | unpark_len += 1; |
| | 15767 | waiter.done.store(true, .release); |
| | 15768 | // `waiter.*` is now potentially invalid so must not be referenced again. |
| 15768 | if (unpark_len == unpark_buf.len) { | 15769 | if (unpark_len == unpark_buf.len) { |
| 15769 | unpark(&unpark_buf, ptr); | 15770 | unpark(&unpark_buf, ptr); |
| 15770 | unpark_len = 0; | 15771 | unpark_len = 0; |
| ... | @@ -15781,13 +15782,14 @@ const parking_futex = struct { | ... | @@ -15781,13 +15782,14 @@ const parking_futex = struct { |
| 15781 | defer bucket.mutex.unlock(); | 15782 | defer bucket.mutex.unlock(); |
| 15782 | bucket.waiters.remove(&waiter.node); | 15783 | bucket.waiters.remove(&waiter.node); |
| 15783 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | 15784 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| | 15785 | waiter.done.store(true, .release); // potentially invalidates `waiter.*` |
| 15784 | } | 15786 | } |
| 15785 | }; | 15787 | }; |
| 15786 | const parking_sleep = struct { | 15788 | const parking_sleep = struct { |
| 15787 | comptime { | 15789 | comptime { |
| 15788 | assert(use_parking_sleep); | 15790 | assert(use_parking_sleep); |
| 15789 | } | 15791 | } |
| 15790 | fn sleep(timeout: Io.Timeout) Io.Cancelable!void { | 15792 | fn sleep(deadline: ?Io.Clock.Timestamp) Io.SleepError!void { |
| 15791 | const opt_thread = Thread.current; | 15793 | const opt_thread = Thread.current; |
| 15792 | cancelable: { | 15794 | cancelable: { |
| 15793 | const thread = opt_thread orelse break :cancelable; | 15795 | const thread = opt_thread orelse break :cancelable; |
| ... | @@ -15796,85 +15798,90 @@ const parking_sleep = struct { | ... | @@ -15796,85 +15798,90 @@ const parking_sleep = struct { |
| 15796 | .unblocked => {}, | 15798 | .unblocked => {}, |
| 15797 | } | 15799 | } |
| 15798 | thread.futex_waiter = null; | 15800 | thread.futex_waiter = null; |
| 15799 | { | 15801 | const orig_status = thread.status.fetchOr( |
| 15800 | const old_status = thread.status.fetchOr( | 15802 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, |
| 15801 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, | 15803 | .release, // release `thread.futex_waiter` |
| 15802 | .release, // release `thread.futex_waiter` | 15804 | ); |
| 15803 | ); | 15805 | switch (orig_status.cancelation) { |
| 15804 | switch (old_status.cancelation) { | 15806 | .none => {}, // status is now `.parked` |
| 15805 | .none => {}, // status is now `.parked` | 15807 | .canceling => return error.Canceled, // status is now `.canceled` |
| 15806 | .canceling => return error.Canceled, // status is now `.canceled` | 15808 | .canceled => break :cancelable, // status is still `.canceled` |
| 15807 | .canceled => break :cancelable, // status is still `.canceled` | 15809 | .parked => unreachable, |
| 15808 | .parked => unreachable, | 15810 | .blocked => unreachable, |
| | 15811 | .blocked_windows_dns => unreachable, |
| | 15812 | .blocked_canceling => unreachable, |
| | 15813 | } |
| | 15814 | while (park(deadline, null)) { |
| | 15815 | // Either a cancelation or a spurious unpark; let's see which! |
| | 15816 | switch (thread.status.load(.monotonic).cancelation) { |
| | 15817 | .parked => continue, // spurious unpark; keep sleeping |
| | 15818 | .canceling => { |
| | 15819 | // We got canceled; update our state and return. |
| | 15820 | thread.status.store( |
| | 15821 | .{ .cancelation = .canceled, .awaitable = orig_status.awaitable }, |
| | 15822 | .monotonic, |
| | 15823 | ); |
| | 15824 | return error.Canceled; |
| | 15825 | }, |
| | 15826 | .none => unreachable, |
| | 15827 | .canceled => unreachable, |
| 15809 | .blocked => unreachable, | 15828 | .blocked => unreachable, |
| 15810 | .blocked_windows_dns => unreachable, | 15829 | .blocked_windows_dns => unreachable, |
| 15811 | .blocked_canceling => unreachable, | 15830 | .blocked_canceling => unreachable, |
| 15812 | } | 15831 | } |
| 15813 | } | | |
| 15814 | if (park(timeout, null)) { | | |
| 15815 | // The only reason this could possibly happen is cancelation. | | |
| 15816 | const old_status = thread.status.load(.monotonic); | | |
| 15817 | assert(old_status.cancelation == .canceling); | | |
| 15818 | thread.status.store( | | |
| 15819 | .{ .cancelation = .canceled, .awaitable = old_status.awaitable }, | | |
| 15820 | .monotonic, | | |
| 15821 | ); | | |
| 15822 | return error.Canceled; | | |
| 15823 | } else |err| switch (err) { | 15832 | } else |err| switch (err) { |
| 15824 | error.Timeout => { | 15833 | error.Timeout => switch (thread.status.fetchAnd( |
| 15825 | // We're not out of the woods yet: an unpark could race with the timeout. | 15834 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, |
| 15826 | const old_status = thread.status.fetchAnd( | 15835 | .monotonic, |
| 15827 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, | 15836 | ).cancelation) { |
| 15828 | .monotonic, | 15837 | // We updated our own status from `.parked` to `.none`. |
| 15829 | ); | 15838 | .parked => return, // new status is `.none` |
| 15830 | switch (old_status.cancelation) { | 15839 | .canceling => { |
| 15831 | .parked => return, // No race; new status is `.none` | 15840 | // Timeout raced with a cancelation. We don't need to do anything, but |
| 15832 | .canceling => { | 15841 | // the next `park` on this thread will see a spurious unpark. |
| 15833 | // Race condition: the timeout was reached, then someone tried to unpark | 15842 | // Status is still `.canceling`. |
| 15834 | // us for a cancelation. Whoever did that will have called `unpark`, so | 15843 | return; |
| 15835 | // drop that unpark request by waiting for it. | 15844 | }, |
| 15836 | // Status is still `.canceling`. | 15845 | .none => unreachable, |
| 15837 | park(.none, null) catch |e| switch (e) { | 15846 | .canceled => unreachable, |
| 15838 | error.Timeout => unreachable, | 15847 | .blocked => unreachable, |
| 15839 | }; | 15848 | .blocked_windows_dns => unreachable, |
| 15840 | return; | 15849 | .blocked_canceling => unreachable, |
| 15841 | }, | | |
| 15842 | .none => unreachable, | | |
| 15843 | .canceled => unreachable, | | |
| 15844 | .blocked => unreachable, | | |
| 15845 | .blocked_windows_dns => unreachable, | | |
| 15846 | .blocked_canceling => unreachable, | | |
| 15847 | } | | |
| 15848 | }, | 15850 | }, |
| 15849 | } | 15851 | } |
| 15850 | } | 15852 | } |
| 15851 | // Uncancelable sleep; we expect not to be manually unparked. | 15853 | // Uncancelable sleep; this case is very simple. |
| 15852 | if (park(timeout, null)) { | 15854 | while (park(deadline, null)) { |
| 15853 | unreachable; // unexpected unpark | 15855 | // Definitely spurious; nothing to do. |
| 15854 | } else |err| switch (err) { | 15856 | } else |err| switch (err) { |
| 15855 | error.Timeout => return, | 15857 | error.Timeout => return, |
| 15856 | } | 15858 | } |
| 15857 | } | 15859 | } |
| | 15860 | /// Sleep for approximately `ms` awake milliseconds in an attempt to work around Windows kernel bugs. |
| | 15861 | fn windowsRetrySleep(ms: u32) (Io.Cancelable || Io.UnexpectedError)!void { |
| | 15862 | const now_timestamp = nowWindows(.awake) catch unreachable; // '.awake' is supported on Windows |
| | 15863 | const deadline = now_timestamp.addDuration(.fromMilliseconds(ms)); |
| | 15864 | parking_sleep.sleep(.{ .raw = deadline, .clock = .awake }) catch |err| switch (err) { |
| | 15865 | error.UnsupportedClock => unreachable, |
| | 15866 | else => |e| return e, |
| | 15867 | }; |
| | 15868 | } |
| 15858 | }; | 15869 | }; |
| 15859 | | 15870 | |
| | 15871 | /// Spurious wakeups are possible. |
| | 15872 | /// |
| 15860 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. | 15873 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. |
| 15861 | fn park(timeout: Io.Timeout, addr_hint: ?*const anyopaque) error{Timeout}!void { | 15874 | fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void { |
| 15862 | comptime assert(use_parking_futex or use_parking_sleep); | 15875 | comptime assert(use_parking_futex or use_parking_sleep); |
| 15863 | switch (builtin.target.os.tag) { | 15876 | switch (builtin.target.os.tag) { |
| 15864 | .windows => { | 15877 | .windows => { |
| 15865 | var timeout_buf: windows.LARGE_INTEGER = undefined; | 15878 | var timeout_buf: windows.LARGE_INTEGER = undefined; |
| 15866 | const raw_timeout: ?*windows.LARGE_INTEGER = timeout: switch (timeout) { | 15879 | const raw_timeout: ?*windows.LARGE_INTEGER = if (opt_deadline) |deadline| timeout: { |
| 15867 | .none => null, | 15880 | const now_timestamp = nowWindows(deadline.clock) catch unreachable; |
| 15868 | .deadline => |timestamp| continue :timeout .{ .duration = .{ | 15881 | const nanoseconds = now_timestamp.durationTo(deadline.raw).nanoseconds; |
| 15869 | .clock = timestamp.clock, | 15882 | timeout_buf = @intCast(@divTrunc(-nanoseconds, 100)); |
| 15870 | .raw = (nowWindows(timestamp.clock) catch unreachable).durationTo(timestamp.raw), | 15883 | break :timeout &timeout_buf; |
| 15871 | } }, | 15884 | } else null; |
| 15872 | .duration => |duration| { | | |
| 15873 | _ = duration.clock; // Windows only supports monotonic | | |
| 15874 | timeout_buf = @intCast(@divTrunc(-duration.raw.nanoseconds, 100)); | | |
| 15875 | break :timeout &timeout_buf; | | |
| 15876 | }, | | |
| 15877 | }; | | |
| 15878 | // `RtlWaitOnAddress` passes the futex address in as the first argument to this call, | 15885 | // `RtlWaitOnAddress` passes the futex address in as the first argument to this call, |
| 15879 | // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId` | 15886 | // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId` |
| 15880 | // does *not* accept the address so the kernel can't really be using it as a hint. An | 15887 | // does *not* accept the address so the kernel can't really be using it as a hint. An |
| ... | @@ -15896,20 +15903,13 @@ fn park(timeout: Io.Timeout, addr_hint: ?*const anyopaque) error{Timeout}!void { | ... | @@ -15896,20 +15903,13 @@ fn park(timeout: Io.Timeout, addr_hint: ?*const anyopaque) error{Timeout}!void { |
| 15896 | }, | 15903 | }, |
| 15897 | .netbsd => { | 15904 | .netbsd => { |
| 15898 | var ts_buf: posix.timespec = undefined; | 15905 | var ts_buf: posix.timespec = undefined; |
| 15899 | const ts: ?*posix.timespec, const abstime: bool, const clock_real: bool = switch (timeout) { | 15906 | const ts: ?*posix.timespec, const clock_real: bool = if (opt_deadline) |deadline| timeout: { |
| 15900 | .none => .{ null, false, false }, | 15907 | ts_buf = timestampToPosix(deadline.raw.nanoseconds); |
| 15901 | .deadline => |timestamp| timeout: { | 15908 | break :timeout .{ &ts_buf, deadline.clock == .real }; |
| 15902 | ts_buf = timestampToPosix(timestamp.raw.nanoseconds); | 15909 | } else .{ null, true }; |
| 15903 | break :timeout .{ &ts_buf, true, timestamp.clock == .real }; | | |
| 15904 | }, | | |
| 15905 | .duration => |duration| timeout: { | | |
| 15906 | ts_buf = timestampToPosix(duration.raw.nanoseconds); | | |
| 15907 | break :timeout .{ &ts_buf, false, duration.clock == .real }; | | |
| 15908 | }, | | |
| 15909 | }; | | |
| 15910 | switch (posix.errno(std.c._lwp_park( | 15910 | switch (posix.errno(std.c._lwp_park( |
| 15911 | if (clock_real) .REALTIME else .MONOTONIC, | 15911 | if (clock_real) .REALTIME else .MONOTONIC, |
| 15912 | .{ .ABSTIME = abstime }, | 15912 | .{ .ABSTIME = true }, |
| 15913 | ts, | 15913 | ts, |
| 15914 | 0, | 15914 | 0, |
| 15915 | addr_hint, | 15915 | addr_hint, |