| ... | ... | @@ -29,8 +29,8 @@ const ws2_32 = std.os.windows.ws2_32; |
| 29 | 29 | /// * scanning environment variables on some targets |
| 30 | 30 | /// * memory-mapping when mmap or equivalent is not available |
| 31 | 31 | allocator: Allocator, |
| 32 | | mutex: std.Thread.Mutex = .{}, |
| 33 | | cond: std.Thread.Condition = .{}, |
| 32 | mutex: Mutex = .init, |
| 33 | cond: Condition = .init, |
| 34 | 34 | run_queue: std.SinglyLinkedList = .{}, |
| 35 | 35 | join_requested: bool = false, |
| 36 | 36 | stack_size: usize, |
| ... | ... | @@ -1486,8 +1486,8 @@ var global_single_threaded_instance: Threaded = .init_single_threaded; |
| 1486 | 1486 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; |
| 1487 | 1487 | |
| 1488 | 1488 | pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { |
| 1489 | | t.mutex.lock(); |
| 1490 | | defer t.mutex.unlock(); |
| 1489 | mutexLockUncancelable(&t.mutex); |
| 1490 | defer mutexUnlock(&t.mutex); |
| 1491 | 1491 | t.async_limit = new_limit; |
| 1492 | 1492 | } |
| 1493 | 1493 | |
| ... | ... | @@ -1508,11 +1508,11 @@ pub fn deinit(t: *Threaded) void { |
| 1508 | 1508 | fn join(t: *Threaded) void { |
| 1509 | 1509 | if (builtin.single_threaded) return; |
| 1510 | 1510 | { |
| 1511 | | t.mutex.lock(); |
| 1512 | | defer t.mutex.unlock(); |
| 1511 | mutexLockUncancelable(&t.mutex); |
| 1512 | defer mutexUnlock(&t.mutex); |
| 1513 | 1513 | t.join_requested = true; |
| 1514 | 1514 | } |
| 1515 | | t.cond.broadcast(); |
| 1515 | condBroadcast(&t.cond); |
| 1516 | 1516 | t.wait_group.wait(); |
| 1517 | 1517 | } |
| 1518 | 1518 | |
| ... | ... | @@ -1574,20 +1574,20 @@ fn worker(t: *Threaded) void { |
| 1574 | 1574 | |
| 1575 | 1575 | defer t.wait_group.finish(); |
| 1576 | 1576 | |
| 1577 | | t.mutex.lock(); |
| 1578 | | defer t.mutex.unlock(); |
| 1577 | mutexLockUncancelable(&t.mutex); |
| 1578 | defer mutexUnlock(&t.mutex); |
| 1579 | 1579 | |
| 1580 | 1580 | while (true) { |
| 1581 | 1581 | while (t.run_queue.popFirst()) |runnable_node| { |
| 1582 | | t.mutex.unlock(); |
| 1582 | mutexUnlock(&t.mutex); |
| 1583 | 1583 | thread.cancel_protection = .unblocked; |
| 1584 | 1584 | const runnable: *Runnable = @fieldParentPtr("node", runnable_node); |
| 1585 | 1585 | runnable.startFn(runnable, &thread, t); |
| 1586 | | t.mutex.lock(); |
| 1586 | mutexLockUncancelable(&t.mutex); |
| 1587 | 1587 | t.busy_count -= 1; |
| 1588 | 1588 | } |
| 1589 | 1589 | if (t.join_requested) break; |
| 1590 | | t.cond.wait(&t.mutex); |
| 1590 | condWait(&t.cond, &t.mutex); |
| 1591 | 1591 | } |
| 1592 | 1592 | } |
| 1593 | 1593 | |
| ... | ... | @@ -2004,12 +2004,12 @@ fn async( |
| 2004 | 2004 | }, |
| 2005 | 2005 | }; |
| 2006 | 2006 | |
| 2007 | | t.mutex.lock(); |
| 2007 | mutexLockUncancelable(&t.mutex); |
| 2008 | 2008 | |
| 2009 | 2009 | const busy_count = t.busy_count; |
| 2010 | 2010 | |
| 2011 | 2011 | if (busy_count >= @intFromEnum(t.async_limit)) { |
| 2012 | | t.mutex.unlock(); |
| 2012 | mutexUnlock(&t.mutex); |
| 2013 | 2013 | future.destroy(gpa); |
| 2014 | 2014 | start(context.ptr, result.ptr); |
| 2015 | 2015 | return null; |
| ... | ... | @@ -2023,7 +2023,7 @@ fn async( |
| 2023 | 2023 | const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch { |
| 2024 | 2024 | t.wait_group.finish(); |
| 2025 | 2025 | t.busy_count = busy_count; |
| 2026 | | t.mutex.unlock(); |
| 2026 | mutexUnlock(&t.mutex); |
| 2027 | 2027 | future.destroy(gpa); |
| 2028 | 2028 | start(context.ptr, result.ptr); |
| 2029 | 2029 | return null; |
| ... | ... | @@ -2033,8 +2033,8 @@ fn async( |
| 2033 | 2033 | |
| 2034 | 2034 | t.run_queue.prepend(&future.runnable.node); |
| 2035 | 2035 | |
| 2036 | | t.mutex.unlock(); |
| 2037 | | t.cond.signal(); |
| 2036 | mutexUnlock(&t.mutex); |
| 2037 | condSignal(&t.cond); |
| 2038 | 2038 | return @ptrCast(future); |
| 2039 | 2039 | } |
| 2040 | 2040 | |
| ... | ... | @@ -2056,8 +2056,8 @@ fn concurrent( |
| 2056 | 2056 | }; |
| 2057 | 2057 | errdefer future.destroy(gpa); |
| 2058 | 2058 | |
| 2059 | | t.mutex.lock(); |
| 2060 | | defer t.mutex.unlock(); |
| 2059 | mutexLockUncancelable(&t.mutex); |
| 2060 | defer mutexUnlock(&t.mutex); |
| 2061 | 2061 | |
| 2062 | 2062 | const busy_count = t.busy_count; |
| 2063 | 2063 | |
| ... | ... | @@ -2080,7 +2080,7 @@ fn concurrent( |
| 2080 | 2080 | |
| 2081 | 2081 | t.run_queue.prepend(&future.runnable.node); |
| 2082 | 2082 | |
| 2083 | | t.cond.signal(); |
| 2083 | condSignal(&t.cond); |
| 2084 | 2084 | return @ptrCast(future); |
| 2085 | 2085 | } |
| 2086 | 2086 | |
| ... | ... | @@ -2101,12 +2101,12 @@ fn groupAsync( |
| 2101 | 2101 | error.OutOfMemory => return groupAsyncEager(start, context.ptr), |
| 2102 | 2102 | }; |
| 2103 | 2103 | |
| 2104 | | t.mutex.lock(); |
| 2104 | mutexLockUncancelable(&t.mutex); |
| 2105 | 2105 | |
| 2106 | 2106 | const busy_count = t.busy_count; |
| 2107 | 2107 | |
| 2108 | 2108 | if (busy_count >= @intFromEnum(t.async_limit)) { |
| 2109 | | t.mutex.unlock(); |
| 2109 | mutexUnlock(&t.mutex); |
| 2110 | 2110 | task.destroy(gpa); |
| 2111 | 2111 | return groupAsyncEager(start, context.ptr); |
| 2112 | 2112 | } |
| ... | ... | @@ -2119,7 +2119,7 @@ fn groupAsync( |
| 2119 | 2119 | const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch { |
| 2120 | 2120 | t.wait_group.finish(); |
| 2121 | 2121 | t.busy_count = busy_count; |
| 2122 | | t.mutex.unlock(); |
| 2122 | mutexUnlock(&t.mutex); |
| 2123 | 2123 | task.destroy(gpa); |
| 2124 | 2124 | return groupAsyncEager(start, context.ptr); |
| 2125 | 2125 | }; |
| ... | ... | @@ -2136,8 +2136,8 @@ fn groupAsync( |
| 2136 | 2136 | }, .monotonic); |
| 2137 | 2137 | t.run_queue.prepend(&task.runnable.node); |
| 2138 | 2138 | |
| 2139 | | t.mutex.unlock(); |
| 2140 | | t.cond.signal(); |
| 2139 | mutexUnlock(&t.mutex); |
| 2140 | condSignal(&t.cond); |
| 2141 | 2141 | } |
| 2142 | 2142 | fn groupAsyncEager( |
| 2143 | 2143 | start: *const fn (context: *const anyopaque) Io.Cancelable!void, |
| ... | ... | @@ -2201,8 +2201,8 @@ fn groupConcurrent( |
| 2201 | 2201 | }; |
| 2202 | 2202 | errdefer task.destroy(gpa); |
| 2203 | 2203 | |
| 2204 | | t.mutex.lock(); |
| 2205 | | defer t.mutex.unlock(); |
| 2204 | mutexLockUncancelable(&t.mutex); |
| 2205 | defer mutexUnlock(&t.mutex); |
| 2206 | 2206 | |
| 2207 | 2207 | const busy_count = t.busy_count; |
| 2208 | 2208 | |
| ... | ... | @@ -2233,7 +2233,7 @@ fn groupConcurrent( |
| 2233 | 2233 | }, .monotonic); |
| 2234 | 2234 | t.run_queue.prepend(&task.runnable.node); |
| 2235 | 2235 | |
| 2236 | | t.cond.signal(); |
| 2236 | condSignal(&t.cond); |
| 2237 | 2237 | } |
| 2238 | 2238 | |
| 2239 | 2239 | fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void { |
| ... | ... | @@ -3838,8 +3838,8 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 3838 | 3838 | |
| 3839 | 3839 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION { |
| 3840 | 3840 | if (!t.system_basic_information.initialized.load(.acquire)) { |
| 3841 | | t.mutex.lock(); |
| 3842 | | defer t.mutex.unlock(); |
| 3841 | mutexLockUncancelable(&t.mutex); |
| 3842 | defer mutexUnlock(&t.mutex); |
| 3843 | 3843 | |
| 3844 | 3844 | switch (windows.ntdll.NtQuerySystemInformation( |
| 3845 | 3845 | .SystemBasicInformation, |
| ... | ... | @@ -14373,8 +14373,8 @@ const WindowsEnvironStrings = struct { |
| 14373 | 14373 | }; |
| 14374 | 14374 | |
| 14375 | 14375 | fn scanEnviron(t: *Threaded) void { |
| 14376 | | t.mutex.lock(); |
| 14377 | | defer t.mutex.unlock(); |
| 14376 | mutexLockUncancelable(&t.mutex); |
| 14377 | defer mutexUnlock(&t.mutex); |
| 14378 | 14378 | |
| 14379 | 14379 | if (t.environ.initialized) return; |
| 14380 | 14380 | t.environ.initialized = true; |
| ... | ... | @@ -14729,8 +14729,8 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14729 | 14729 | |
| 14730 | 14730 | fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 14731 | 14731 | { |
| 14732 | | t.mutex.lock(); |
| 14733 | | defer t.mutex.unlock(); |
| 14732 | mutexLockUncancelable(&t.mutex); |
| 14733 | defer mutexUnlock(&t.mutex); |
| 14734 | 14734 | if (t.null_file.fd != -1) return t.null_file.fd; |
| 14735 | 14735 | } |
| 14736 | 14736 | const mode: u32 = 0; |
| ... | ... | @@ -14741,8 +14741,8 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 14741 | 14741 | .SUCCESS => { |
| 14742 | 14742 | syscall.finish(); |
| 14743 | 14743 | const fresh_fd: posix.fd_t = @intCast(rc); |
| 14744 | | t.mutex.lock(); // Another thread might have won the race. |
| 14745 | | defer t.mutex.unlock(); |
| 14744 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 14745 | defer mutexUnlock(&t.mutex); |
| 14746 | 14746 | if (t.null_file.fd != -1) { |
| 14747 | 14747 | posix.close(fresh_fd); |
| 14748 | 14748 | return t.null_file.fd; |
| ... | ... | @@ -15402,8 +15402,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15402 | 15402 | |
| 15403 | 15403 | fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15404 | 15404 | { |
| 15405 | | t.mutex.lock(); |
| 15406 | | defer t.mutex.unlock(); |
| 15405 | mutexLockUncancelable(&t.mutex); |
| 15406 | defer mutexUnlock(&t.mutex); |
| 15407 | 15407 | if (t.random_file.handle) |handle| return handle; |
| 15408 | 15408 | } |
| 15409 | 15409 | |
| ... | ... | @@ -15437,8 +15437,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15437 | 15437 | )) { |
| 15438 | 15438 | .SUCCESS => { |
| 15439 | 15439 | syscall.finish(); |
| 15440 | | t.mutex.lock(); // Another thread might have won the race. |
| 15441 | | defer t.mutex.unlock(); |
| 15440 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 15441 | defer mutexUnlock(&t.mutex); |
| 15442 | 15442 | if (t.random_file.handle) |prev_handle| { |
| 15443 | 15443 | windows.CloseHandle(fresh_handle); |
| 15444 | 15444 | return prev_handle; |
| ... | ... | @@ -15458,8 +15458,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15458 | 15458 | |
| 15459 | 15459 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15460 | 15460 | { |
| 15461 | | t.mutex.lock(); |
| 15462 | | defer t.mutex.unlock(); |
| 15461 | mutexLockUncancelable(&t.mutex); |
| 15462 | defer mutexUnlock(&t.mutex); |
| 15463 | 15463 | if (t.null_file.handle) |handle| return handle; |
| 15464 | 15464 | } |
| 15465 | 15465 | |
| ... | ... | @@ -15505,8 +15505,8 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15505 | 15505 | )) { |
| 15506 | 15506 | .SUCCESS => { |
| 15507 | 15507 | syscall.finish(); |
| 15508 | | t.mutex.lock(); // Another thread might have won the race. |
| 15509 | | defer t.mutex.unlock(); |
| 15508 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 15509 | defer mutexUnlock(&t.mutex); |
| 15510 | 15510 | if (t.null_file.handle) |prev_handle| { |
| 15511 | 15511 | windows.CloseHandle(fresh_handle); |
| 15512 | 15512 | return prev_handle; |
| ... | ... | @@ -16551,15 +16551,15 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| 16551 | 16551 | } |
| 16552 | 16552 | |
| 16553 | 16553 | fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 16554 | | t.mutex.lock(); |
| 16555 | | defer t.mutex.unlock(); |
| 16554 | mutexLockUncancelable(&t.mutex); |
| 16555 | defer mutexUnlock(&t.mutex); |
| 16556 | 16556 | |
| 16557 | 16557 | if (!t.csprng.isInitialized()) { |
| 16558 | 16558 | @branchHint(.unlikely); |
| 16559 | 16559 | var seed: [Csprng.seed_len]u8 = undefined; |
| 16560 | 16560 | { |
| 16561 | | t.mutex.unlock(); |
| 16562 | | defer t.mutex.lock(); |
| 16561 | mutexUnlock(&t.mutex); |
| 16562 | defer mutexLockUncancelable(&t.mutex); |
| 16563 | 16563 | |
| 16564 | 16564 | const prev = swapCancelProtection(t, .blocked); |
| 16565 | 16565 | defer _ = swapCancelProtection(t, prev); |
| ... | ... | @@ -16744,8 +16744,8 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 16744 | 16744 | |
| 16745 | 16745 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16746 | 16746 | { |
| 16747 | | t.mutex.lock(); |
| 16748 | | defer t.mutex.unlock(); |
| 16747 | mutexLockUncancelable(&t.mutex); |
| 16748 | defer mutexUnlock(&t.mutex); |
| 16749 | 16749 | |
| 16750 | 16750 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| 16751 | 16751 | if (t.random_file.fd != -1) return t.random_file.fd; |
| ... | ... | @@ -16785,8 +16785,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16785 | 16785 | .SUCCESS => { |
| 16786 | 16786 | syscall.finish(); |
| 16787 | 16787 | if (!statx.mask.TYPE) return error.EntropyUnavailable; |
| 16788 | | t.mutex.lock(); // Another thread might have won the race. |
| 16789 | | defer t.mutex.unlock(); |
| 16788 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16789 | defer mutexUnlock(&t.mutex); |
| 16790 | 16790 | if (t.random_file.fd >= 0) { |
| 16791 | 16791 | posix.close(fd); |
| 16792 | 16792 | return t.random_file.fd; |
| ... | ... | @@ -16813,8 +16813,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16813 | 16813 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 16814 | 16814 | .SUCCESS => { |
| 16815 | 16815 | syscall.finish(); |
| 16816 | | t.mutex.lock(); // Another thread might have won the race. |
| 16817 | | defer t.mutex.unlock(); |
| 16816 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16817 | defer mutexUnlock(&t.mutex); |
| 16818 | 16818 | if (t.random_file.fd >= 0) { |
| 16819 | 16819 | posix.close(fd); |
| 16820 | 16820 | return t.random_file.fd; |
| ... | ... | @@ -16878,13 +16878,13 @@ const parking_futex = struct { |
| 16878 | 16878 | /// avoid a race. |
| 16879 | 16879 | num_waiters: std.atomic.Value(u32), |
| 16880 | 16880 | /// Protects `waiters`. |
| 16881 | | mutex: std.Thread.Mutex, |
| 16881 | mutex: Mutex, |
| 16882 | 16882 | waiters: std.DoublyLinkedList, |
| 16883 | 16883 | |
| 16884 | 16884 | /// Prevent false sharing between buckets. |
| 16885 | 16885 | _: void align(std.atomic.cache_line) = {}, |
| 16886 | 16886 | |
| 16887 | | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} }; |
| 16887 | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .init, .waiters = .{} }; |
| 16888 | 16888 | }; |
| 16889 | 16889 | |
| 16890 | 16890 | const Waiter = struct { |
| ... | ... | @@ -16947,8 +16947,8 @@ const parking_futex = struct { |
| 16947 | 16947 | var status_buf: std.atomic.Value(Thread.Status) = undefined; |
| 16948 | 16948 | |
| 16949 | 16949 | { |
| 16950 | | bucket.mutex.lock(); |
| 16951 | | defer bucket.mutex.unlock(); |
| 16950 | mutexLockUncancelable(&bucket.mutex); |
| 16951 | defer mutexUnlock(&bucket.mutex); |
| 16952 | 16952 | |
| 16953 | 16953 | _ = bucket.num_waiters.fetchAdd(1, .acquire); |
| 16954 | 16954 | |
| ... | ... | @@ -17017,8 +17017,8 @@ const parking_futex = struct { |
| 17017 | 17017 | .parked => { |
| 17018 | 17018 | // We saw a timeout and updated our own status from `.parked` to `.none`. It is |
| 17019 | 17019 | // our responsibility to remove `waiter` from `bucket`. |
| 17020 | | bucket.mutex.lock(); |
| 17021 | | defer bucket.mutex.unlock(); |
| 17020 | mutexLockUncancelable(&bucket.mutex); |
| 17021 | defer mutexUnlock(&bucket.mutex); |
| 17022 | 17022 | bucket.waiters.remove(&waiter.node); |
| 17023 | 17023 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17024 | 17024 | }, |
| ... | ... | @@ -17057,8 +17057,8 @@ const parking_futex = struct { |
| 17057 | 17057 | // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`. |
| 17058 | 17058 | var waking_head: ?*std.DoublyLinkedList.Node = null; |
| 17059 | 17059 | { |
| 17060 | | bucket.mutex.lock(); |
| 17061 | | defer bucket.mutex.unlock(); |
| 17060 | mutexLockUncancelable(&bucket.mutex); |
| 17061 | defer mutexUnlock(&bucket.mutex); |
| 17062 | 17062 | |
| 17063 | 17063 | var num_removed: u32 = 0; |
| 17064 | 17064 | var it = bucket.waiters.first; |
| ... | ... | @@ -17113,8 +17113,8 @@ const parking_futex = struct { |
| 17113 | 17113 | |
| 17114 | 17114 | fn removeCanceledWaiter(waiter: *Waiter) void { |
| 17115 | 17115 | const bucket = bucketForAddress(waiter.address); |
| 17116 | | bucket.mutex.lock(); |
| 17117 | | defer bucket.mutex.unlock(); |
| 17116 | mutexLockUncancelable(&bucket.mutex); |
| 17117 | defer mutexUnlock(&bucket.mutex); |
| 17118 | 17118 | bucket.waiters.remove(&waiter.node); |
| 17119 | 17119 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17120 | 17120 | waiter.done.store(true, .release); // potentially invalidates `waiter.*` |
| ... | ... | @@ -18102,3 +18102,141 @@ fn eventSet(event: *Io.Event) void { |
| 18102 | 18102 | .waiting => Thread.futexWake(@ptrCast(event), std.math.maxInt(u32)), |
| 18103 | 18103 | } |
| 18104 | 18104 | } |
| 18105 | |
| 18106 | const Condition = if (!is_windows) Io.Condition else struct { |
| 18107 | condition: windows.CONDITION_VARIABLE, |
| 18108 | const init: @This() = .{ .condition = .{} }; |
| 18109 | }; |
| 18110 | |
| 18111 | /// Same as `Io.Condition.broadcast` but avoids the VTable. |
| 18112 | fn condBroadcast(cond: *Condition) void { |
| 18113 | if (is_windows) return windows.ntdll.RtlWakeAllConditionVariable(&cond.condition); |
| 18114 | var prev_state = cond.state.load(.monotonic); |
| 18115 | while (prev_state.waiters > prev_state.signals) { |
| 18116 | @branchHint(.unlikely); |
| 18117 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18118 | .waiters = prev_state.waiters, |
| 18119 | .signals = prev_state.waiters, |
| 18120 | }, .release, .monotonic) orelse { |
| 18121 | // Update the epoch to tell the waiting threads that there are new signals for them. |
| 18122 | // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen |
| 18123 | // between it observing the epoch and sleeping on it, but this is extraordinarily |
| 18124 | // unlikely due to the precise number of calls required. |
| 18125 | _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update |
| 18126 | Thread.futexWake(&cond.epoch.raw, prev_state.waiters - prev_state.signals); |
| 18127 | return; |
| 18128 | }; |
| 18129 | } |
| 18130 | } |
| 18131 | |
| 18132 | /// Same as `Io.Condition.signal` but avoids the VTable. |
| 18133 | fn condSignal(cond: *Condition) void { |
| 18134 | if (is_windows) return windows.ntdll.RtlWakeConditionVariable(&cond.condition); |
| 18135 | var prev_state = cond.state.load(.monotonic); |
| 18136 | while (prev_state.waiters > prev_state.signals) { |
| 18137 | @branchHint(.unlikely); |
| 18138 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18139 | .waiters = prev_state.waiters, |
| 18140 | .signals = prev_state.signals + 1, |
| 18141 | }, .release, .monotonic) orelse { |
| 18142 | // Update the epoch to tell the waiting threads that there are new signals for them. |
| 18143 | // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen |
| 18144 | // between it observing the epoch and sleeping on it, but this is extraordinarily |
| 18145 | // unlikely due to the precise number of calls required. |
| 18146 | _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update |
| 18147 | Thread.futexWake(&cond.epoch.raw, 1); |
| 18148 | return; |
| 18149 | }; |
| 18150 | } |
| 18151 | } |
| 18152 | |
| 18153 | /// Same as `Io.Condition.waitUncancelable` but avoids the VTable. |
| 18154 | fn condWait(cond: *Condition, mutex: *Mutex) void { |
| 18155 | if (is_windows) { |
| 18156 | _ = windows.kernel32.SleepConditionVariableSRW(&cond.condition, &mutex.srwlock, windows.INFINITE, 0); |
| 18157 | return; |
| 18158 | } |
| 18159 | var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load |
| 18160 | |
| 18161 | { |
| 18162 | const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 18163 | assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters |
| 18164 | } |
| 18165 | |
| 18166 | mutexUnlock(mutex); |
| 18167 | defer mutexLockUncancelable(mutex); |
| 18168 | |
| 18169 | while (true) { |
| 18170 | Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null); |
| 18171 | |
| 18172 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod |
| 18173 | |
| 18174 | var prev_state = cond.state.load(.monotonic); |
| 18175 | while (prev_state.signals > 0) { |
| 18176 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18177 | .waiters = prev_state.waiters - 1, |
| 18178 | .signals = prev_state.signals - 1, |
| 18179 | }, .acquire, .monotonic) orelse { |
| 18180 | // We successfully consumed a signal. |
| 18181 | return; |
| 18182 | }; |
| 18183 | } |
| 18184 | } |
| 18185 | } |
| 18186 | |
| 18187 | const Mutex = if (!is_windows) Io.Mutex else struct { |
| 18188 | srwlock: windows.SRWLOCK, |
| 18189 | const init: @This() = .{ .srwlock = .{} }; |
| 18190 | }; |
| 18191 | |
| 18192 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. |
| 18193 | fn mutexLock(m: *Io.Mutex) Io.Cancelable!void { |
| 18194 | const initial_state = m.state.cmpxchgWeak( |
| 18195 | .unlocked, |
| 18196 | .locked_once, |
| 18197 | .acquire, |
| 18198 | .monotonic, |
| 18199 | ) orelse { |
| 18200 | @branchHint(.likely); |
| 18201 | return; |
| 18202 | }; |
| 18203 | if (initial_state == .contended) { |
| 18204 | try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18205 | } |
| 18206 | while (m.state.swap(.contended, .acquire) != .unlocked) { |
| 18207 | try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18208 | } |
| 18209 | } |
| 18210 | |
| 18211 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. |
| 18212 | fn mutexLockUncancelable(m: *Mutex) void { |
| 18213 | if (is_windows) return windows.ntdll.RtlAcquireSRWLockExclusive(&m.srwlock); |
| 18214 | const initial_state = m.state.cmpxchgWeak( |
| 18215 | .unlocked, |
| 18216 | .locked_once, |
| 18217 | .acquire, |
| 18218 | .monotonic, |
| 18219 | ) orelse { |
| 18220 | @branchHint(.likely); |
| 18221 | return; |
| 18222 | }; |
| 18223 | if (initial_state == .contended) { |
| 18224 | Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18225 | } |
| 18226 | while (m.state.swap(.contended, .acquire) != .unlocked) { |
| 18227 | Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18228 | } |
| 18229 | } |
| 18230 | |
| 18231 | /// Same as `Io.Mutex.unlock` but avoids the VTable. |
| 18232 | fn mutexUnlock(m: *Mutex) void { |
| 18233 | if (is_windows) return windows.ntdll.RtlReleaseSRWLockExclusive(&m.srwlock); |
| 18234 | switch (m.state.swap(.unlocked, .release)) { |
| 18235 | .unlocked => unreachable, |
| 18236 | .locked_once => {}, |
| 18237 | .contended => { |
| 18238 | @branchHint(.unlikely); |
| 18239 | Thread.futexWake(@ptrCast(&m.state.raw), 1); |
| 18240 | }, |
| 18241 | } |
| 18242 | } |