| ... | ... | @@ -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: Io.Mutex = .init, |
| 33 | cond: Io.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, |
| ... | ... | @@ -14299,10 +14299,9 @@ const Wsa = struct { |
| 14299 | 14299 | }; |
| 14300 | 14300 | |
| 14301 | 14301 | fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void { |
| 14302 | | const t_io = io(t); |
| 14303 | 14302 | const wsa = &t.wsa; |
| 14304 | | try wsa.mutex.lock(t_io); |
| 14305 | | defer wsa.mutex.unlock(t_io); |
| 14303 | try mutexLock(&wsa.mutex); |
| 14304 | defer mutexUnlock(&wsa.mutex); |
| 14306 | 14305 | switch (wsa.status) { |
| 14307 | 14306 | .uninitialized => { |
| 14308 | 14307 | var wsa_data: ws2_32.WSADATA = undefined; |
| ... | ... | @@ -14373,8 +14372,8 @@ const WindowsEnvironStrings = struct { |
| 14373 | 14372 | }; |
| 14374 | 14373 | |
| 14375 | 14374 | fn scanEnviron(t: *Threaded) void { |
| 14376 | | t.mutex.lock(); |
| 14377 | | defer t.mutex.unlock(); |
| 14375 | mutexLockUncancelable(&t.mutex); |
| 14376 | defer mutexUnlock(&t.mutex); |
| 14378 | 14377 | |
| 14379 | 14378 | if (t.environ.initialized) return; |
| 14380 | 14379 | t.environ.initialized = true; |
| ... | ... | @@ -14729,8 +14728,8 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14729 | 14728 | |
| 14730 | 14729 | fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 14731 | 14730 | { |
| 14732 | | t.mutex.lock(); |
| 14733 | | defer t.mutex.unlock(); |
| 14731 | mutexLockUncancelable(&t.mutex); |
| 14732 | defer mutexUnlock(&t.mutex); |
| 14734 | 14733 | if (t.null_file.fd != -1) return t.null_file.fd; |
| 14735 | 14734 | } |
| 14736 | 14735 | const mode: u32 = 0; |
| ... | ... | @@ -14741,8 +14740,8 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 14741 | 14740 | .SUCCESS => { |
| 14742 | 14741 | syscall.finish(); |
| 14743 | 14742 | const fresh_fd: posix.fd_t = @intCast(rc); |
| 14744 | | t.mutex.lock(); // Another thread might have won the race. |
| 14745 | | defer t.mutex.unlock(); |
| 14743 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 14744 | defer mutexUnlock(&t.mutex); |
| 14746 | 14745 | if (t.null_file.fd != -1) { |
| 14747 | 14746 | posix.close(fresh_fd); |
| 14748 | 14747 | return t.null_file.fd; |
| ... | ... | @@ -15402,8 +15401,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15402 | 15401 | |
| 15403 | 15402 | fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15404 | 15403 | { |
| 15405 | | t.mutex.lock(); |
| 15406 | | defer t.mutex.unlock(); |
| 15404 | mutexLockUncancelable(&t.mutex); |
| 15405 | defer mutexUnlock(&t.mutex); |
| 15407 | 15406 | if (t.random_file.handle) |handle| return handle; |
| 15408 | 15407 | } |
| 15409 | 15408 | |
| ... | ... | @@ -15437,8 +15436,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15437 | 15436 | )) { |
| 15438 | 15437 | .SUCCESS => { |
| 15439 | 15438 | syscall.finish(); |
| 15440 | | t.mutex.lock(); // Another thread might have won the race. |
| 15441 | | defer t.mutex.unlock(); |
| 15439 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 15440 | defer mutexUnlock(&t.mutex); |
| 15442 | 15441 | if (t.random_file.handle) |prev_handle| { |
| 15443 | 15442 | windows.CloseHandle(fresh_handle); |
| 15444 | 15443 | return prev_handle; |
| ... | ... | @@ -15458,8 +15457,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15458 | 15457 | |
| 15459 | 15458 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15460 | 15459 | { |
| 15461 | | t.mutex.lock(); |
| 15462 | | defer t.mutex.unlock(); |
| 15460 | mutexLockUncancelable(&t.mutex); |
| 15461 | defer mutexUnlock(&t.mutex); |
| 15463 | 15462 | if (t.null_file.handle) |handle| return handle; |
| 15464 | 15463 | } |
| 15465 | 15464 | |
| ... | ... | @@ -15505,8 +15504,8 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15505 | 15504 | )) { |
| 15506 | 15505 | .SUCCESS => { |
| 15507 | 15506 | syscall.finish(); |
| 15508 | | t.mutex.lock(); // Another thread might have won the race. |
| 15509 | | defer t.mutex.unlock(); |
| 15507 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 15508 | defer mutexUnlock(&t.mutex); |
| 15510 | 15509 | if (t.null_file.handle) |prev_handle| { |
| 15511 | 15510 | windows.CloseHandle(fresh_handle); |
| 15512 | 15511 | return prev_handle; |
| ... | ... | @@ -16551,15 +16550,15 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| 16551 | 16550 | } |
| 16552 | 16551 | |
| 16553 | 16552 | fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 16554 | | t.mutex.lock(); |
| 16555 | | defer t.mutex.unlock(); |
| 16553 | mutexLockUncancelable(&t.mutex); |
| 16554 | defer mutexUnlock(&t.mutex); |
| 16556 | 16555 | |
| 16557 | 16556 | if (!t.csprng.isInitialized()) { |
| 16558 | 16557 | @branchHint(.unlikely); |
| 16559 | 16558 | var seed: [Csprng.seed_len]u8 = undefined; |
| 16560 | 16559 | { |
| 16561 | | t.mutex.unlock(); |
| 16562 | | defer t.mutex.lock(); |
| 16560 | mutexUnlock(&t.mutex); |
| 16561 | defer mutexLockUncancelable(&t.mutex); |
| 16563 | 16562 | |
| 16564 | 16563 | const prev = swapCancelProtection(t, .blocked); |
| 16565 | 16564 | defer _ = swapCancelProtection(t, prev); |
| ... | ... | @@ -16744,8 +16743,8 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 16744 | 16743 | |
| 16745 | 16744 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16746 | 16745 | { |
| 16747 | | t.mutex.lock(); |
| 16748 | | defer t.mutex.unlock(); |
| 16746 | mutexLockUncancelable(&t.mutex); |
| 16747 | defer mutexUnlock(&t.mutex); |
| 16749 | 16748 | |
| 16750 | 16749 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| 16751 | 16750 | if (t.random_file.fd != -1) return t.random_file.fd; |
| ... | ... | @@ -16785,8 +16784,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16785 | 16784 | .SUCCESS => { |
| 16786 | 16785 | syscall.finish(); |
| 16787 | 16786 | if (!statx.mask.TYPE) return error.EntropyUnavailable; |
| 16788 | | t.mutex.lock(); // Another thread might have won the race. |
| 16789 | | defer t.mutex.unlock(); |
| 16787 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16788 | defer mutexUnlock(&t.mutex); |
| 16790 | 16789 | if (t.random_file.fd >= 0) { |
| 16791 | 16790 | posix.close(fd); |
| 16792 | 16791 | return t.random_file.fd; |
| ... | ... | @@ -16813,8 +16812,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 16813 | 16812 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 16814 | 16813 | .SUCCESS => { |
| 16815 | 16814 | syscall.finish(); |
| 16816 | | t.mutex.lock(); // Another thread might have won the race. |
| 16817 | | defer t.mutex.unlock(); |
| 16815 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16816 | defer mutexUnlock(&t.mutex); |
| 16818 | 16817 | if (t.random_file.fd >= 0) { |
| 16819 | 16818 | posix.close(fd); |
| 16820 | 16819 | return t.random_file.fd; |
| ... | ... | @@ -16878,13 +16877,13 @@ const parking_futex = struct { |
| 16878 | 16877 | /// avoid a race. |
| 16879 | 16878 | num_waiters: std.atomic.Value(u32), |
| 16880 | 16879 | /// Protects `waiters`. |
| 16881 | | mutex: std.Thread.Mutex, |
| 16880 | mutex: Io.Mutex, |
| 16882 | 16881 | waiters: std.DoublyLinkedList, |
| 16883 | 16882 | |
| 16884 | 16883 | /// Prevent false sharing between buckets. |
| 16885 | 16884 | _: void align(std.atomic.cache_line) = {}, |
| 16886 | 16885 | |
| 16887 | | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} }; |
| 16886 | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .init, .waiters = .{} }; |
| 16888 | 16887 | }; |
| 16889 | 16888 | |
| 16890 | 16889 | const Waiter = struct { |
| ... | ... | @@ -16947,8 +16946,8 @@ const parking_futex = struct { |
| 16947 | 16946 | var status_buf: std.atomic.Value(Thread.Status) = undefined; |
| 16948 | 16947 | |
| 16949 | 16948 | { |
| 16950 | | bucket.mutex.lock(); |
| 16951 | | defer bucket.mutex.unlock(); |
| 16949 | mutexLockUncancelable(&bucket.mutex); |
| 16950 | defer mutexUnlock(&bucket.mutex); |
| 16952 | 16951 | |
| 16953 | 16952 | _ = bucket.num_waiters.fetchAdd(1, .acquire); |
| 16954 | 16953 | |
| ... | ... | @@ -17017,8 +17016,8 @@ const parking_futex = struct { |
| 17017 | 17016 | .parked => { |
| 17018 | 17017 | // We saw a timeout and updated our own status from `.parked` to `.none`. It is |
| 17019 | 17018 | // our responsibility to remove `waiter` from `bucket`. |
| 17020 | | bucket.mutex.lock(); |
| 17021 | | defer bucket.mutex.unlock(); |
| 17019 | mutexLockUncancelable(&bucket.mutex); |
| 17020 | defer mutexUnlock(&bucket.mutex); |
| 17022 | 17021 | bucket.waiters.remove(&waiter.node); |
| 17023 | 17022 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17024 | 17023 | }, |
| ... | ... | @@ -17057,8 +17056,8 @@ const parking_futex = struct { |
| 17057 | 17056 | // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`. |
| 17058 | 17057 | var waking_head: ?*std.DoublyLinkedList.Node = null; |
| 17059 | 17058 | { |
| 17060 | | bucket.mutex.lock(); |
| 17061 | | defer bucket.mutex.unlock(); |
| 17059 | mutexLockUncancelable(&bucket.mutex); |
| 17060 | defer mutexUnlock(&bucket.mutex); |
| 17062 | 17061 | |
| 17063 | 17062 | var num_removed: u32 = 0; |
| 17064 | 17063 | var it = bucket.waiters.first; |
| ... | ... | @@ -17113,8 +17112,8 @@ const parking_futex = struct { |
| 17113 | 17112 | |
| 17114 | 17113 | fn removeCanceledWaiter(waiter: *Waiter) void { |
| 17115 | 17114 | const bucket = bucketForAddress(waiter.address); |
| 17116 | | bucket.mutex.lock(); |
| 17117 | | defer bucket.mutex.unlock(); |
| 17115 | mutexLockUncancelable(&bucket.mutex); |
| 17116 | defer mutexUnlock(&bucket.mutex); |
| 17118 | 17117 | bucket.waiters.remove(&waiter.node); |
| 17119 | 17118 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); |
| 17120 | 17119 | waiter.done.store(true, .release); // potentially invalidates `waiter.*` |
| ... | ... | @@ -18102,3 +18101,123 @@ fn eventSet(event: *Io.Event) void { |
| 18102 | 18101 | .waiting => Thread.futexWake(@ptrCast(event), std.math.maxInt(u32)), |
| 18103 | 18102 | } |
| 18104 | 18103 | } |
| 18104 | |
| 18105 | /// Same as `Io.Condition.broadcast` but avoids the VTable. |
| 18106 | fn condBroadcast(cond: *Io.Condition) void { |
| 18107 | var prev_state = cond.state.load(.monotonic); |
| 18108 | while (prev_state.waiters > prev_state.signals) { |
| 18109 | @branchHint(.unlikely); |
| 18110 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18111 | .waiters = prev_state.waiters, |
| 18112 | .signals = prev_state.waiters, |
| 18113 | }, .release, .monotonic) orelse { |
| 18114 | // Update the epoch to tell the waiting threads that there are new signals for them. |
| 18115 | // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen |
| 18116 | // between it observing the epoch and sleeping on it, but this is extraordinarily |
| 18117 | // unlikely due to the precise number of calls required. |
| 18118 | _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update |
| 18119 | Thread.futexWake(&cond.epoch.raw, prev_state.waiters - prev_state.signals); |
| 18120 | return; |
| 18121 | }; |
| 18122 | } |
| 18123 | } |
| 18124 | |
| 18125 | /// Same as `Io.Condition.signal` but avoids the VTable. |
| 18126 | fn condSignal(cond: *Io.Condition) void { |
| 18127 | var prev_state = cond.state.load(.monotonic); |
| 18128 | while (prev_state.waiters > prev_state.signals) { |
| 18129 | @branchHint(.unlikely); |
| 18130 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18131 | .waiters = prev_state.waiters, |
| 18132 | .signals = prev_state.signals + 1, |
| 18133 | }, .release, .monotonic) orelse { |
| 18134 | // Update the epoch to tell the waiting threads that there are new signals for them. |
| 18135 | // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen |
| 18136 | // between it observing the epoch and sleeping on it, but this is extraordinarily |
| 18137 | // unlikely due to the precise number of calls required. |
| 18138 | _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update |
| 18139 | Thread.futexWake(&cond.epoch.raw, 1); |
| 18140 | return; |
| 18141 | }; |
| 18142 | } |
| 18143 | } |
| 18144 | |
| 18145 | /// Same as `Io.Condition.waitUncancelable` but avoids the VTable. |
| 18146 | fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { |
| 18147 | var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load |
| 18148 | |
| 18149 | { |
| 18150 | const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic); |
| 18151 | assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters |
| 18152 | } |
| 18153 | |
| 18154 | mutexUnlock(mutex); |
| 18155 | defer mutexLockUncancelable(mutex); |
| 18156 | |
| 18157 | while (true) { |
| 18158 | Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null); |
| 18159 | |
| 18160 | epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod |
| 18161 | |
| 18162 | var prev_state = cond.state.load(.monotonic); |
| 18163 | while (prev_state.signals > 0) { |
| 18164 | prev_state = cond.state.cmpxchgWeak(prev_state, .{ |
| 18165 | .waiters = prev_state.waiters - 1, |
| 18166 | .signals = prev_state.signals - 1, |
| 18167 | }, .acquire, .monotonic) orelse { |
| 18168 | // We successfully consumed a signal. |
| 18169 | return; |
| 18170 | }; |
| 18171 | } |
| 18172 | } |
| 18173 | } |
| 18174 | |
| 18175 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. |
| 18176 | fn mutexLock(m: *Io.Mutex) Io.Cancelable!void { |
| 18177 | const initial_state = m.state.cmpxchgWeak( |
| 18178 | .unlocked, |
| 18179 | .locked_once, |
| 18180 | .acquire, |
| 18181 | .monotonic, |
| 18182 | ) orelse { |
| 18183 | @branchHint(.likely); |
| 18184 | return; |
| 18185 | }; |
| 18186 | if (initial_state == .contended) { |
| 18187 | try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18188 | } |
| 18189 | while (m.state.swap(.contended, .acquire) != .unlocked) { |
| 18190 | try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18191 | } |
| 18192 | } |
| 18193 | |
| 18194 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. |
| 18195 | fn mutexLockUncancelable(m: *Io.Mutex) void { |
| 18196 | const initial_state = m.state.cmpxchgWeak( |
| 18197 | .unlocked, |
| 18198 | .locked_once, |
| 18199 | .acquire, |
| 18200 | .monotonic, |
| 18201 | ) orelse { |
| 18202 | @branchHint(.likely); |
| 18203 | return; |
| 18204 | }; |
| 18205 | if (initial_state == .contended) { |
| 18206 | Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18207 | } |
| 18208 | while (m.state.swap(.contended, .acquire) != .unlocked) { |
| 18209 | Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null); |
| 18210 | } |
| 18211 | } |
| 18212 | |
| 18213 | /// Same as `Io.Mutex.unlock` but avoids the VTable. |
| 18214 | fn mutexUnlock(m: *Io.Mutex) void { |
| 18215 | switch (m.state.swap(.unlocked, .release)) { |
| 18216 | .unlocked => unreachable, |
| 18217 | .locked_once => {}, |
| 18218 | .contended => { |
| 18219 | @branchHint(.unlikely); |
| 18220 | Thread.futexWake(@ptrCast(&m.state.raw), 1); |
| 18221 | }, |
| 18222 | } |
| 18223 | } |