| ... | @@ -4955,7 +4955,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -4955,7 +4955,10 @@ pub fn dirOpenFileWtf16( |
| 4955 | // kernel bug with retry attempts. | 4955 | // kernel bug with retry attempts. |
| 4956 | syscall.finish(); | 4956 | syscall.finish(); |
| 4957 | if (max_attempts - attempt == 0) return error.FileBusy; | 4957 | if (max_attempts - attempt == 0) return error.FileBusy; |
| 4958 | try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1); | 4958 | try parking_sleep.sleep(.{ .duration = .{ |
| | 4959 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), |
| | 4960 | .clock = .awake, |
| | 4961 | } }); |
| 4959 | attempt += 1; | 4962 | attempt += 1; |
| 4960 | syscall = try .start(); | 4963 | syscall = try .start(); |
| 4961 | continue; | 4964 | continue; |
| ... | @@ -4977,7 +4980,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -4977,7 +4980,10 @@ pub fn dirOpenFileWtf16( |
| 4977 | // fixed by sleeping and retrying until the error goes away. | 4980 | // fixed by sleeping and retrying until the error goes away. |
| 4978 | syscall.finish(); | 4981 | syscall.finish(); |
| 4979 | if (max_attempts - attempt == 0) return error.FileBusy; | 4982 | if (max_attempts - attempt == 0) return error.FileBusy; |
| 4980 | try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1); | 4983 | try parking_sleep.sleep(.{ .duration = .{ |
| | 4984 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), |
| | 4985 | .clock = .awake, |
| | 4986 | } }); |
| 4981 | attempt += 1; | 4987 | attempt += 1; |
| 4982 | syscall = try .start(); | 4988 | syscall = try .start(); |
| 4983 | continue; | 4989 | continue; |
| ... | @@ -10823,9 +10829,6 @@ fn nowPosix(clock: Io.Clock) Io.Timestamp { | ... | @@ -10823,9 +10829,6 @@ fn nowPosix(clock: Io.Clock) Io.Timestamp { |
| 10823 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Timestamp { | 10829 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Timestamp { |
| 10824 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 10830 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10825 | _ = t; | 10831 | _ = t; |
| 10826 | return nowInner(clock); | | |
| 10827 | } | | |
| 10828 | fn nowInner(clock: Io.Clock) Io.Timestamp { | | |
| 10829 | return switch (native_os) { | 10832 | return switch (native_os) { |
| 10830 | .windows => nowWindows(clock), | 10833 | .windows => nowWindows(clock), |
| 10831 | .wasi => nowWasi(clock), | 10834 | .wasi => nowWasi(clock), |
| ... | @@ -15582,7 +15585,10 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -15582,7 +15585,10 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15582 | // this other than retrying the creation after the OS finishes | 15585 | // this other than retrying the creation after the OS finishes |
| 15583 | // the deletion. | 15586 | // the deletion. |
| 15584 | syscall.finish(); | 15587 | syscall.finish(); |
| 15585 | try parking_sleep.windowsRetrySleep(1); | 15588 | try parking_sleep.sleep(.{ .duration = .{ |
| | 15589 | .raw = .fromMilliseconds(1), |
| | 15590 | .clock = .awake, |
| | 15591 | } }); |
| 15586 | syscall = try .start(); | 15592 | syscall = try .start(); |
| 15587 | continue; | 15593 | continue; |
| 15588 | }, | 15594 | }, |
| ... | @@ -16955,13 +16961,9 @@ const parking_futex = struct { | ... | @@ -16955,13 +16961,9 @@ const parking_futex = struct { |
| 16955 | /// | 16961 | /// |
| 16956 | /// * Removing the `Waiter` from `Bucket.waiters` | 16962 | /// * Removing the `Waiter` from `Bucket.waiters` |
| 16957 | /// * Decrementing `Bucket.num_waiters` | 16963 | /// * Decrementing `Bucket.num_waiters` |
| 16958 | /// * Atomically setting `done` (after this, the `Waiter` may go out of scope at any time, | 16964 | /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope |
| 16959 | /// so must not be referenced again) | 16965 | /// while it is still in the `Bucket`). |
| 16960 | /// * Unparking the thread (last, so that the unparked thread definitely sees `done`) | | |
| 16961 | thread_status: *std.atomic.Value(Thread.Status), | 16966 | thread_status: *std.atomic.Value(Thread.Status), |
| 16962 | /// Initially `false`. Whoever updates `thread_status` to `.none`/`.canceling` will update | | |
| 16963 | /// this to `true` once they are done with the `Waiter`, just before unparking `tid`. | | |
| 16964 | done: std.atomic.Value(bool), | | |
| 16965 | }; | 16967 | }; |
| 16966 | | 16968 | |
| 16967 | fn bucketForAddress(address: usize) *Bucket { | 16969 | fn bucketForAddress(address: usize) *Bucket { |
| ... | @@ -17000,7 +17002,6 @@ const parking_futex = struct { | ... | @@ -17000,7 +17002,6 @@ const parking_futex = struct { |
| 17000 | .address = @intFromPtr(ptr), | 17002 | .address = @intFromPtr(ptr), |
| 17001 | .tid = self_tid, | 17003 | .tid = self_tid, |
| 17002 | .thread_status = undefined, // populated in critical section | 17004 | .thread_status = undefined, // populated in critical section |
| 17003 | .done = .init(false), | | |
| 17004 | }; | 17005 | }; |
| 17005 | | 17006 | |
| 17006 | var status_buf: std.atomic.Value(Thread.Status) = undefined; | 17007 | var status_buf: std.atomic.Value(Thread.Status) = undefined; |
| ... | @@ -17058,44 +17059,41 @@ const parking_futex = struct { | ... | @@ -17058,44 +17059,41 @@ const parking_futex = struct { |
| 17058 | bucket.waiters.append(&waiter.node); | 17059 | bucket.waiters.append(&waiter.node); |
| 17059 | } | 17060 | } |
| 17060 | | 17061 | |
| 17061 | const deadline: ?Io.Clock.Timestamp = switch (timeout) { | 17062 | if (park(timeout, ptr)) { |
| 17062 | .none => null, | 17063 | // We were unparked by either `wake` or cancelation, so our current status is either |
| 17063 | .duration => |d| .{ | 17064 | // `.none` or `.canceling`. In either case, they've already removed `waiter` from |
| 17064 | .raw = nowInner(d.clock).addDuration(d.raw), | 17065 | // `bucket`, so we have nothing more to do! |
| 17065 | .clock = d.clock, | | |
| 17066 | }, | | |
| 17067 | .deadline => |d| d, | | |
| 17068 | }; | | |
| 17069 | while (park(deadline, ptr)) { | | |
| 17070 | if (waiter.done.load(.acquire)) return; // all done! | | |
| 17071 | } else |err| switch (err) { | 17066 | } else |err| switch (err) { |
| 17072 | error.Timeout => switch (waiter.thread_status.fetchAnd( | 17067 | error.Timeout => { |
| 17073 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, | 17068 | // We're not out of the woods yet: an unpark could race with the timeout. |
| 17074 | .monotonic, | 17069 | const old_status = waiter.thread_status.fetchAnd( |
| 17075 | ).cancelation) { | 17070 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, |
| 17076 | .parked => { | 17071 | .monotonic, |
| 17077 | // We saw a timeout and updated our own status from `.parked` to `.none`. It is | 17072 | ); |
| 17078 | // our responsibility to remove `waiter` from `bucket`. | 17073 | switch (old_status.cancelation) { |
| 17079 | mutexLock(&bucket.mutex); | 17074 | .parked => { |
| 17080 | defer mutexUnlock(&bucket.mutex); | 17075 | // No race. It is our responsibility to remove `waiter` from `bucket`. |
| 17081 | bucket.waiters.remove(&waiter.node); | 17076 | // New status is `.none`. |
| 17082 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | 17077 | bucket.mutex.lock(); |
| 17083 | }, | 17078 | defer bucket.mutex.unlock(); |
| 17084 | .none, .canceling => { | 17079 | bucket.waiters.remove(&waiter.node); |
| 17085 | // Race condition: the timeout was reached, then `wake` or a cancelation tried | 17080 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17086 | // to update our status. They won the race, so wait for them to do the cleanup. | 17081 | }, |
| 17087 | // They'll tell us by setting `waiter.done` and unparking us. | 17082 | .none, .canceling => { |
| 17088 | while (!waiter.done.load(.acquire)) { | 17083 | // Race condition: the timeout was reached, then `wake` or a canceler tried |
| 17089 | park(null, ptr) catch |e| switch (e) { | 17084 | // to unpark us. Whoever did that will remove us from `bucket`. Wait for |
| | 17085 | // that (and drop the unpark request in doing so). |
| | 17086 | // New status is `.none` or `.canceling` respectively. |
| | 17087 | park(.none, ptr) catch |e| switch (e) { |
| 17090 | error.Timeout => unreachable, | 17088 | error.Timeout => unreachable, |
| 17091 | }; | 17089 | }; |
| 17092 | } | 17090 | }, |
| 17093 | }, | 17091 | .canceled => unreachable, |
| 17094 | .canceled => unreachable, | 17092 | .blocked => unreachable, |
| 17095 | .blocked => unreachable, | 17093 | .blocked_alertable => unreachable, |
| 17096 | .blocked_alertable => unreachable, | 17094 | .blocked_canceling => unreachable, |
| 17097 | .blocked_alertable_canceling => unreachable, | 17095 | .blocked_alertable_canceling => unreachable, |
| 17098 | .blocked_canceling => unreachable, | 17096 | } |
| 17099 | }, | 17097 | }, |
| 17100 | } | 17098 | } |
| 17101 | } | 17099 | } |
| ... | @@ -17144,6 +17142,9 @@ const parking_futex = struct { | ... | @@ -17144,6 +17142,9 @@ const parking_futex = struct { |
| 17144 | waiter.node.next = waking_head; | 17142 | waiter.node.next = waking_head; |
| 17145 | waking_head = &waiter.node; | 17143 | waking_head = &waiter.node; |
| 17146 | num_removed += 1; | 17144 | num_removed += 1; |
| | 17145 | // Signal to `waiter` that they're about to be unparked, in case we're racing with their |
| | 17146 | // timeout. See corresponding logic in `wake`. |
| | 17147 | waiter.address = 0; |
| 17147 | } | 17148 | } |
| 17148 | | 17149 | |
| 17149 | _ = bucket.num_waiters.fetchSub(num_removed, .monotonic); | 17150 | _ = bucket.num_waiters.fetchSub(num_removed, .monotonic); |
| ... | @@ -17158,8 +17159,6 @@ const parking_futex = struct { | ... | @@ -17158,8 +17159,6 @@ const parking_futex = struct { |
| 17158 | const waiter: *Waiter = @fieldParentPtr("node", node); | 17159 | const waiter: *Waiter = @fieldParentPtr("node", node); |
| 17159 | unpark_buf[unpark_len] = waiter.tid; | 17160 | unpark_buf[unpark_len] = waiter.tid; |
| 17160 | unpark_len += 1; | 17161 | unpark_len += 1; |
| 17161 | waiter.done.store(true, .release); | | |
| 17162 | // `waiter.*` is now potentially invalid so must not be referenced again. | | |
| 17163 | if (unpark_len == unpark_buf.len) { | 17162 | if (unpark_len == unpark_buf.len) { |
| 17164 | unpark(&unpark_buf, ptr); | 17163 | unpark(&unpark_buf, ptr); |
| 17165 | unpark_len = 0; | 17164 | unpark_len = 0; |
| ... | @@ -17176,14 +17175,13 @@ const parking_futex = struct { | ... | @@ -17176,14 +17175,13 @@ const parking_futex = struct { |
| 17176 | defer mutexUnlock(&bucket.mutex); | 17175 | defer mutexUnlock(&bucket.mutex); |
| 17177 | bucket.waiters.remove(&waiter.node); | 17176 | bucket.waiters.remove(&waiter.node); |
| 17178 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | 17177 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17179 | waiter.done.store(true, .release); // potentially invalidates `waiter.*` | | |
| 17180 | } | 17178 | } |
| 17181 | }; | 17179 | }; |
| 17182 | const parking_sleep = struct { | 17180 | const parking_sleep = struct { |
| 17183 | comptime { | 17181 | comptime { |
| 17184 | assert(use_parking_sleep); | 17182 | assert(use_parking_sleep); |
| 17185 | } | 17183 | } |
| 17186 | fn sleep(deadline: ?Io.Clock.Timestamp) Io.Cancelable!void { | 17184 | fn sleep(timeout: Io.Timeout) Io.Cancelable!void { |
| 17187 | const opt_thread = Thread.current; | 17185 | const opt_thread = Thread.current; |
| 17188 | cancelable: { | 17186 | cancelable: { |
| 17189 | const thread = opt_thread orelse break :cancelable; | 17187 | const thread = opt_thread orelse break :cancelable; |
| ... | @@ -17192,90 +17190,87 @@ const parking_sleep = struct { | ... | @@ -17192,90 +17190,87 @@ const parking_sleep = struct { |
| 17192 | .unblocked => {}, | 17190 | .unblocked => {}, |
| 17193 | } | 17191 | } |
| 17194 | thread.futex_waiter = null; | 17192 | thread.futex_waiter = null; |
| 17195 | const orig_status = thread.status.fetchOr( | 17193 | { |
| 17196 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, | 17194 | const old_status = thread.status.fetchOr( |
| 17197 | .release, // release `thread.futex_waiter` | 17195 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, |
| 17198 | ); | 17196 | .release, // release `thread.futex_waiter` |
| 17199 | switch (orig_status.cancelation) { | 17197 | ); |
| 17200 | .none => {}, // status is now `.parked` | 17198 | switch (old_status.cancelation) { |
| 17201 | .canceling => return error.Canceled, // status is now `.canceled` | 17199 | .none => {}, // status is now `.parked` |
| 17202 | .canceled => break :cancelable, // status is still `.canceled` | 17200 | .canceling => return error.Canceled, // status is now `.canceled` |
| 17203 | .parked => unreachable, | 17201 | .canceled => break :cancelable, // status is still `.canceled` |
| 17204 | .blocked => unreachable, | 17202 | .parked => unreachable, |
| 17205 | .blocked_alertable => unreachable, | | |
| 17206 | .blocked_alertable_canceling => unreachable, | | |
| 17207 | .blocked_canceling => unreachable, | | |
| 17208 | } | | |
| 17209 | while (park(deadline, null)) { | | |
| 17210 | // Either a cancelation or a spurious unpark; let's see which! | | |
| 17211 | switch (thread.status.load(.monotonic).cancelation) { | | |
| 17212 | .parked => continue, // spurious unpark; keep sleeping | | |
| 17213 | .canceling => { | | |
| 17214 | // We got canceled; update our state and return. | | |
| 17215 | thread.status.store( | | |
| 17216 | .{ .cancelation = .canceled, .awaitable = orig_status.awaitable }, | | |
| 17217 | .monotonic, | | |
| 17218 | ); | | |
| 17219 | return error.Canceled; | | |
| 17220 | }, | | |
| 17221 | .none => unreachable, | | |
| 17222 | .canceled => unreachable, | | |
| 17223 | .blocked => unreachable, | 17203 | .blocked => unreachable, |
| 17224 | .blocked_alertable => unreachable, | 17204 | .blocked_alertable => unreachable, |
| 17225 | .blocked_alertable_canceling => unreachable, | 17205 | .blocked_alertable_canceling => unreachable, |
| 17226 | .blocked_canceling => unreachable, | 17206 | .blocked_canceling => unreachable, |
| 17227 | } | 17207 | } |
| 17228 | } else |err| switch (err) { | 17208 | } |
| 17229 | error.Timeout => switch (thread.status.fetchAnd( | 17209 | if (park(timeout, null)) { |
| 17230 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, | 17210 | // The only reason this could possibly happen is cancelation. |
| | 17211 | const old_status = thread.status.load(.monotonic); |
| | 17212 | assert(old_status.cancelation == .canceling); |
| | 17213 | thread.status.store( |
| | 17214 | .{ .cancelation = .canceled, .awaitable = old_status.awaitable }, |
| 17231 | .monotonic, | 17215 | .monotonic, |
| 17232 | ).cancelation) { | 17216 | ); |
| 17233 | // We updated our own status from `.parked` to `.none`. | 17217 | return error.Canceled; |
| 17234 | .parked => return, // new status is `.none` | 17218 | } else |err| switch (err) { |
| 17235 | .canceling => { | 17219 | error.Timeout => { |
| 17236 | // Timeout raced with a cancelation. We don't need to do anything, but | 17220 | // We're not out of the woods yet: an unpark could race with the timeout. |
| 17237 | // the next `park` on this thread will see a spurious unpark. | 17221 | const old_status = thread.status.fetchAnd( |
| 17238 | // Status is still `.canceling`. | 17222 | .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones }, |
| 17239 | return; | 17223 | .monotonic, |
| 17240 | }, | 17224 | ); |
| 17241 | .none => unreachable, | 17225 | switch (old_status.cancelation) { |
| 17242 | .canceled => unreachable, | 17226 | .parked => return, // No race; new status is `.none` |
| 17243 | .blocked => unreachable, | 17227 | .canceling => { |
| 17244 | .blocked_alertable => unreachable, | 17228 | // Race condition: the timeout was reached, then someone tried to unpark |
| 17245 | .blocked_alertable_canceling => unreachable, | 17229 | // us for a cancelation. Whoever did that will have called `unpark`, so |
| 17246 | .blocked_canceling => unreachable, | 17230 | // drop that unpark request by waiting for it. |
| | 17231 | // Status is still `.canceling`. |
| | 17232 | park(.none, null) catch |e| switch (e) { |
| | 17233 | error.Timeout => unreachable, |
| | 17234 | }; |
| | 17235 | return; |
| | 17236 | }, |
| | 17237 | .none => unreachable, |
| | 17238 | .canceled => unreachable, |
| | 17239 | .blocked => unreachable, |
| | 17240 | .blocked_alertable => unreachable, |
| | 17241 | .blocked_canceling => unreachable, |
| | 17242 | .blocked_alertable_canceling => unreachable, |
| | 17243 | } |
| 17247 | }, | 17244 | }, |
| 17248 | } | 17245 | } |
| 17249 | } | 17246 | } |
| 17250 | // Uncancelable sleep; this case is very simple. | 17247 | // Uncancelable sleep; we expect not to be manually unparked. |
| 17251 | while (park(deadline, null)) { | 17248 | if (park(timeout, null)) { |
| 17252 | // Definitely spurious; nothing to do. | 17249 | unreachable; // unexpected unpark |
| 17253 | } else |err| switch (err) { | 17250 | } else |err| switch (err) { |
| 17254 | error.Timeout => return, | 17251 | error.Timeout => return, |
| 17255 | } | 17252 | } |
| 17256 | } | 17253 | } |
| 17257 | /// Sleep for approximately `ms` awake milliseconds in an attempt to work around Windows kernel bugs. | | |
| 17258 | fn windowsRetrySleep(ms: u32) (Io.Cancelable || Io.UnexpectedError)!void { | | |
| 17259 | const now_timestamp = nowWindows(.awake); // '.awake' is supported on Windows | | |
| 17260 | const deadline = now_timestamp.addDuration(.fromMilliseconds(ms)); | | |
| 17261 | try parking_sleep.sleep(.{ .raw = deadline, .clock = .awake }); | | |
| 17262 | } | | |
| 17263 | }; | 17254 | }; |
| 17264 | | 17255 | |
| 17265 | /// Spurious wakeups are possible. | | |
| 17266 | /// | | |
| 17267 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. | 17256 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. |
| 17268 | fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void { | 17257 | fn park(timeout: Io.Timeout, addr_hint: ?*const anyopaque) error{Timeout}!void { |
| 17269 | comptime assert(use_parking_futex or use_parking_sleep); | 17258 | comptime assert(use_parking_futex or use_parking_sleep); |
| 17270 | switch (native_os) { | 17259 | switch (native_os) { |
| 17271 | .windows => { | 17260 | .windows => { |
| 17272 | var timeout_buf: windows.LARGE_INTEGER = undefined; | 17261 | var timeout_buf: windows.LARGE_INTEGER = undefined; |
| 17273 | const raw_timeout: ?*windows.LARGE_INTEGER = if (opt_deadline) |deadline| timeout: { | 17262 | const raw_timeout: ?*windows.LARGE_INTEGER = timeout: switch (timeout) { |
| 17274 | const now_timestamp = nowWindows(deadline.clock); | 17263 | .none => null, |
| 17275 | const nanoseconds = now_timestamp.durationTo(deadline.raw).nanoseconds; | 17264 | .deadline => |timestamp| continue :timeout .{ .duration = .{ |
| 17276 | timeout_buf = @intCast(@divTrunc(-nanoseconds, 100)); | 17265 | .clock = timestamp.clock, |
| 17277 | break :timeout &timeout_buf; | 17266 | .raw = (nowWindows(timestamp.clock) catch unreachable).durationTo(timestamp.raw), |
| 17278 | } else null; | 17267 | } }, |
| | 17268 | .duration => |duration| { |
| | 17269 | _ = duration.clock; // Windows only supports monotonic |
| | 17270 | timeout_buf = @intCast(@divTrunc(-duration.raw.nanoseconds, 100)); |
| | 17271 | break :timeout &timeout_buf; |
| | 17272 | }, |
| | 17273 | }; |
| 17279 | // `RtlWaitOnAddress` passes the futex address in as the first argument to this call, | 17274 | // `RtlWaitOnAddress` passes the futex address in as the first argument to this call, |
| 17280 | // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId` | 17275 | // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId` |
| 17281 | // does *not* accept the address so the kernel can't really be using it as a hint. An | 17276 | // does *not* accept the address so the kernel can't really be using it as a hint. An |
| ... | @@ -17297,13 +17292,20 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T | ... | @@ -17297,13 +17292,20 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T |
| 17297 | }, | 17292 | }, |
| 17298 | .netbsd => { | 17293 | .netbsd => { |
| 17299 | var ts_buf: posix.timespec = undefined; | 17294 | var ts_buf: posix.timespec = undefined; |
| 17300 | const ts: ?*posix.timespec, const clock_real: bool = if (opt_deadline) |deadline| timeout: { | 17295 | const ts: ?*posix.timespec, const abstime: bool, const clock_real: bool = switch (timeout) { |
| 17301 | ts_buf = timestampToPosix(deadline.raw.nanoseconds); | 17296 | .none => .{ null, false, false }, |
| 17302 | break :timeout .{ &ts_buf, deadline.clock == .real }; | 17297 | .deadline => |timestamp| timeout: { |
| 17303 | } else .{ null, true }; | 17298 | ts_buf = timestampToPosix(timestamp.raw.nanoseconds); |
| | 17299 | break :timeout .{ &ts_buf, true, timestamp.clock == .real }; |
| | 17300 | }, |
| | 17301 | .duration => |duration| timeout: { |
| | 17302 | ts_buf = timestampToPosix(duration.raw.nanoseconds); |
| | 17303 | break :timeout .{ &ts_buf, false, duration.clock == .real }; |
| | 17304 | }, |
| | 17305 | }; |
| 17304 | switch (posix.errno(std.c._lwp_park( | 17306 | switch (posix.errno(std.c._lwp_park( |
| 17305 | if (clock_real) .REALTIME else .MONOTONIC, | 17307 | if (clock_real) .REALTIME else .MONOTONIC, |
| 17306 | .{ .ABSTIME = true }, | 17308 | .{ .ABSTIME = abstime }, |
| 17307 | ts, | 17309 | ts, |
| 17308 | 0, | 17310 | 0, |
| 17309 | addr_hint, | 17311 | addr_hint, |