authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-01 16:13:51-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-02 14:18:04-08:00
logeb74e23e7ba4f2000bbe4e908bf74c5c9dd7adcc
tree866527741a2cd1b8a707253e31b7f3b59dd2d32a
parentc2d4806d659abf8c4c0ab989eae225303de57af3

std.Io.Threaded: sever dependency on std.Thread Mutex and Condition


1 files changed, 186 insertions(+), 67 deletions(-)

lib/std/Io/Threaded.zig+186-67
...@@ -29,8 +29,8 @@ const ws2_32 = std.os.windows.ws2_32;...@@ -29,8 +29,8 @@ const ws2_32 = std.os.windows.ws2_32;
29/// * scanning environment variables on some targets29/// * scanning environment variables on some targets
30/// * memory-mapping when mmap or equivalent is not available30/// * memory-mapping when mmap or equivalent is not available
31allocator: Allocator,31allocator: Allocator,
32mutex: std.Thread.Mutex = .{},32mutex: Io.Mutex = .init,
33cond: std.Thread.Condition = .{},33cond: Io.Condition = .init,
34run_queue: std.SinglyLinkedList = .{},34run_queue: std.SinglyLinkedList = .{},
35join_requested: bool = false,35join_requested: bool = false,
36stack_size: usize,36stack_size: usize,
...@@ -1486,8 +1486,8 @@ var global_single_threaded_instance: Threaded = .init_single_threaded;...@@ -1486,8 +1486,8 @@ var global_single_threaded_instance: Threaded = .init_single_threaded;
1486pub const global_single_threaded: *Threaded = &global_single_threaded_instance;1486pub const global_single_threaded: *Threaded = &global_single_threaded_instance;
14871487
1488pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {1488pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
1489 t.mutex.lock();1489 mutexLockUncancelable(&t.mutex);
1490 defer t.mutex.unlock();1490 defer mutexUnlock(&t.mutex);
1491 t.async_limit = new_limit;1491 t.async_limit = new_limit;
1492}1492}
14931493
...@@ -1508,11 +1508,11 @@ pub fn deinit(t: *Threaded) void {...@@ -1508,11 +1508,11 @@ pub fn deinit(t: *Threaded) void {
1508fn join(t: *Threaded) void {1508fn join(t: *Threaded) void {
1509 if (builtin.single_threaded) return;1509 if (builtin.single_threaded) return;
1510 {1510 {
1511 t.mutex.lock();1511 mutexLockUncancelable(&t.mutex);
1512 defer t.mutex.unlock();1512 defer mutexUnlock(&t.mutex);
1513 t.join_requested = true;1513 t.join_requested = true;
1514 }1514 }
1515 t.cond.broadcast();1515 condBroadcast(&t.cond);
1516 t.wait_group.wait();1516 t.wait_group.wait();
1517}1517}
15181518
...@@ -1574,20 +1574,20 @@ fn worker(t: *Threaded) void {...@@ -1574,20 +1574,20 @@ fn worker(t: *Threaded) void {
15741574
1575 defer t.wait_group.finish();1575 defer t.wait_group.finish();
15761576
1577 t.mutex.lock();1577 mutexLockUncancelable(&t.mutex);
1578 defer t.mutex.unlock();1578 defer mutexUnlock(&t.mutex);
15791579
1580 while (true) {1580 while (true) {
1581 while (t.run_queue.popFirst()) |runnable_node| {1581 while (t.run_queue.popFirst()) |runnable_node| {
1582 t.mutex.unlock();1582 mutexUnlock(&t.mutex);
1583 thread.cancel_protection = .unblocked;1583 thread.cancel_protection = .unblocked;
1584 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);1584 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);
1585 runnable.startFn(runnable, &thread, t);1585 runnable.startFn(runnable, &thread, t);
1586 t.mutex.lock();1586 mutexLockUncancelable(&t.mutex);
1587 t.busy_count -= 1;1587 t.busy_count -= 1;
1588 }1588 }
1589 if (t.join_requested) break;1589 if (t.join_requested) break;
1590 t.cond.wait(&t.mutex);1590 condWait(&t.cond, &t.mutex);
1591 }1591 }
1592}1592}
15931593
...@@ -2004,12 +2004,12 @@ fn async(...@@ -2004,12 +2004,12 @@ fn async(
2004 },2004 },
2005 };2005 };
20062006
2007 t.mutex.lock();2007 mutexLockUncancelable(&t.mutex);
20082008
2009 const busy_count = t.busy_count;2009 const busy_count = t.busy_count;
20102010
2011 if (busy_count >= @intFromEnum(t.async_limit)) {2011 if (busy_count >= @intFromEnum(t.async_limit)) {
2012 t.mutex.unlock();2012 mutexUnlock(&t.mutex);
2013 future.destroy(gpa);2013 future.destroy(gpa);
2014 start(context.ptr, result.ptr);2014 start(context.ptr, result.ptr);
2015 return null;2015 return null;
...@@ -2023,7 +2023,7 @@ fn async(...@@ -2023,7 +2023,7 @@ fn async(
2023 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {2023 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
2024 t.wait_group.finish();2024 t.wait_group.finish();
2025 t.busy_count = busy_count;2025 t.busy_count = busy_count;
2026 t.mutex.unlock();2026 mutexUnlock(&t.mutex);
2027 future.destroy(gpa);2027 future.destroy(gpa);
2028 start(context.ptr, result.ptr);2028 start(context.ptr, result.ptr);
2029 return null;2029 return null;
...@@ -2033,8 +2033,8 @@ fn async(...@@ -2033,8 +2033,8 @@ fn async(
20332033
2034 t.run_queue.prepend(&future.runnable.node);2034 t.run_queue.prepend(&future.runnable.node);
20352035
2036 t.mutex.unlock();2036 mutexUnlock(&t.mutex);
2037 t.cond.signal();2037 condSignal(&t.cond);
2038 return @ptrCast(future);2038 return @ptrCast(future);
2039}2039}
20402040
...@@ -2056,8 +2056,8 @@ fn concurrent(...@@ -2056,8 +2056,8 @@ fn concurrent(
2056 };2056 };
2057 errdefer future.destroy(gpa);2057 errdefer future.destroy(gpa);
20582058
2059 t.mutex.lock();2059 mutexLockUncancelable(&t.mutex);
2060 defer t.mutex.unlock();2060 defer mutexUnlock(&t.mutex);
20612061
2062 const busy_count = t.busy_count;2062 const busy_count = t.busy_count;
20632063
...@@ -2080,7 +2080,7 @@ fn concurrent(...@@ -2080,7 +2080,7 @@ fn concurrent(
20802080
2081 t.run_queue.prepend(&future.runnable.node);2081 t.run_queue.prepend(&future.runnable.node);
20822082
2083 t.cond.signal();2083 condSignal(&t.cond);
2084 return @ptrCast(future);2084 return @ptrCast(future);
2085}2085}
20862086
...@@ -2101,12 +2101,12 @@ fn groupAsync(...@@ -2101,12 +2101,12 @@ fn groupAsync(
2101 error.OutOfMemory => return groupAsyncEager(start, context.ptr),2101 error.OutOfMemory => return groupAsyncEager(start, context.ptr),
2102 };2102 };
21032103
2104 t.mutex.lock();2104 mutexLockUncancelable(&t.mutex);
21052105
2106 const busy_count = t.busy_count;2106 const busy_count = t.busy_count;
21072107
2108 if (busy_count >= @intFromEnum(t.async_limit)) {2108 if (busy_count >= @intFromEnum(t.async_limit)) {
2109 t.mutex.unlock();2109 mutexUnlock(&t.mutex);
2110 task.destroy(gpa);2110 task.destroy(gpa);
2111 return groupAsyncEager(start, context.ptr);2111 return groupAsyncEager(start, context.ptr);
2112 }2112 }
...@@ -2119,7 +2119,7 @@ fn groupAsync(...@@ -2119,7 +2119,7 @@ fn groupAsync(
2119 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {2119 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
2120 t.wait_group.finish();2120 t.wait_group.finish();
2121 t.busy_count = busy_count;2121 t.busy_count = busy_count;
2122 t.mutex.unlock();2122 mutexUnlock(&t.mutex);
2123 task.destroy(gpa);2123 task.destroy(gpa);
2124 return groupAsyncEager(start, context.ptr);2124 return groupAsyncEager(start, context.ptr);
2125 };2125 };
...@@ -2136,8 +2136,8 @@ fn groupAsync(...@@ -2136,8 +2136,8 @@ fn groupAsync(
2136 }, .monotonic);2136 }, .monotonic);
2137 t.run_queue.prepend(&task.runnable.node);2137 t.run_queue.prepend(&task.runnable.node);
21382138
2139 t.mutex.unlock();2139 mutexUnlock(&t.mutex);
2140 t.cond.signal();2140 condSignal(&t.cond);
2141}2141}
2142fn groupAsyncEager(2142fn groupAsyncEager(
2143 start: *const fn (context: *const anyopaque) Io.Cancelable!void,2143 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
...@@ -2201,8 +2201,8 @@ fn groupConcurrent(...@@ -2201,8 +2201,8 @@ fn groupConcurrent(
2201 };2201 };
2202 errdefer task.destroy(gpa);2202 errdefer task.destroy(gpa);
22032203
2204 t.mutex.lock();2204 mutexLockUncancelable(&t.mutex);
2205 defer t.mutex.unlock();2205 defer mutexUnlock(&t.mutex);
22062206
2207 const busy_count = t.busy_count;2207 const busy_count = t.busy_count;
22082208
...@@ -2233,7 +2233,7 @@ fn groupConcurrent(...@@ -2233,7 +2233,7 @@ fn groupConcurrent(
2233 }, .monotonic);2233 }, .monotonic);
2234 t.run_queue.prepend(&task.runnable.node);2234 t.run_queue.prepend(&task.runnable.node);
22352235
2236 t.cond.signal();2236 condSignal(&t.cond);
2237}2237}
22382238
2239fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {2239fn 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,8 +3838,8 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
38383838
3839fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {3839fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
3840 if (!t.system_basic_information.initialized.load(.acquire)) {3840 if (!t.system_basic_information.initialized.load(.acquire)) {
3841 t.mutex.lock();3841 mutexLockUncancelable(&t.mutex);
3842 defer t.mutex.unlock();3842 defer mutexUnlock(&t.mutex);
38433843
3844 switch (windows.ntdll.NtQuerySystemInformation(3844 switch (windows.ntdll.NtQuerySystemInformation(
3845 .SystemBasicInformation,3845 .SystemBasicInformation,
...@@ -14299,10 +14299,9 @@ const Wsa = struct {...@@ -14299,10 +14299,9 @@ const Wsa = struct {
14299};14299};
1430014300
14301fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {14301fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
14302 const t_io = io(t);
14303 const wsa = &t.wsa;14302 const wsa = &t.wsa;
14304 try wsa.mutex.lock(t_io);14303 try mutexLock(&wsa.mutex);
14305 defer wsa.mutex.unlock(t_io);14304 defer mutexUnlock(&wsa.mutex);
14306 switch (wsa.status) {14305 switch (wsa.status) {
14307 .uninitialized => {14306 .uninitialized => {
14308 var wsa_data: ws2_32.WSADATA = undefined;14307 var wsa_data: ws2_32.WSADATA = undefined;
...@@ -14373,8 +14372,8 @@ const WindowsEnvironStrings = struct {...@@ -14373,8 +14372,8 @@ const WindowsEnvironStrings = struct {
14373};14372};
1437414373
14375fn scanEnviron(t: *Threaded) void {14374fn scanEnviron(t: *Threaded) void {
14376 t.mutex.lock();14375 mutexLockUncancelable(&t.mutex);
14377 defer t.mutex.unlock();14376 defer mutexUnlock(&t.mutex);
1437814377
14379 if (t.environ.initialized) return;14378 if (t.environ.initialized) return;
14380 t.environ.initialized = true;14379 t.environ.initialized = true;
...@@ -14729,8 +14728,8 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp...@@ -14729,8 +14728,8 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1472914728
14730fn getDevNullFd(t: *Threaded) !posix.fd_t {14729fn getDevNullFd(t: *Threaded) !posix.fd_t {
14731 {14730 {
14732 t.mutex.lock();14731 mutexLockUncancelable(&t.mutex);
14733 defer t.mutex.unlock();14732 defer mutexUnlock(&t.mutex);
14734 if (t.null_file.fd != -1) return t.null_file.fd;14733 if (t.null_file.fd != -1) return t.null_file.fd;
14735 }14734 }
14736 const mode: u32 = 0;14735 const mode: u32 = 0;
...@@ -14741,8 +14740,8 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {...@@ -14741,8 +14740,8 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
14741 .SUCCESS => {14740 .SUCCESS => {
14742 syscall.finish();14741 syscall.finish();
14743 const fresh_fd: posix.fd_t = @intCast(rc);14742 const fresh_fd: posix.fd_t = @intCast(rc);
14744 t.mutex.lock(); // Another thread might have won the race.14743 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
14745 defer t.mutex.unlock();14744 defer mutexUnlock(&t.mutex);
14746 if (t.null_file.fd != -1) {14745 if (t.null_file.fd != -1) {
14747 posix.close(fresh_fd);14746 posix.close(fresh_fd);
14748 return t.null_file.fd;14747 return t.null_file.fd;
...@@ -15402,8 +15401,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -15402,8 +15401,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1540215401
15403fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {15402fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
15404 {15403 {
15405 t.mutex.lock();15404 mutexLockUncancelable(&t.mutex);
15406 defer t.mutex.unlock();15405 defer mutexUnlock(&t.mutex);
15407 if (t.random_file.handle) |handle| return handle;15406 if (t.random_file.handle) |handle| return handle;
15408 }15407 }
1540915408
...@@ -15437,8 +15436,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {...@@ -15437,8 +15436,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
15437 )) {15436 )) {
15438 .SUCCESS => {15437 .SUCCESS => {
15439 syscall.finish();15438 syscall.finish();
15440 t.mutex.lock(); // Another thread might have won the race.15439 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
15441 defer t.mutex.unlock();15440 defer mutexUnlock(&t.mutex);
15442 if (t.random_file.handle) |prev_handle| {15441 if (t.random_file.handle) |prev_handle| {
15443 windows.CloseHandle(fresh_handle);15442 windows.CloseHandle(fresh_handle);
15444 return prev_handle;15443 return prev_handle;
...@@ -15458,8 +15457,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {...@@ -15458,8 +15457,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1545815457
15459fn getNulHandle(t: *Threaded) !windows.HANDLE {15458fn getNulHandle(t: *Threaded) !windows.HANDLE {
15460 {15459 {
15461 t.mutex.lock();15460 mutexLockUncancelable(&t.mutex);
15462 defer t.mutex.unlock();15461 defer mutexUnlock(&t.mutex);
15463 if (t.null_file.handle) |handle| return handle;15462 if (t.null_file.handle) |handle| return handle;
15464 }15463 }
1546515464
...@@ -15505,8 +15504,8 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE {...@@ -15505,8 +15504,8 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE {
15505 )) {15504 )) {
15506 .SUCCESS => {15505 .SUCCESS => {
15507 syscall.finish();15506 syscall.finish();
15508 t.mutex.lock(); // Another thread might have won the race.15507 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
15509 defer t.mutex.unlock();15508 defer mutexUnlock(&t.mutex);
15510 if (t.null_file.handle) |prev_handle| {15509 if (t.null_file.handle) |prev_handle| {
15511 windows.CloseHandle(fresh_handle);15510 windows.CloseHandle(fresh_handle);
15512 return prev_handle;15511 return prev_handle;
...@@ -16551,15 +16550,15 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void {...@@ -16551,15 +16550,15 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void {
16551}16550}
1655216551
16553fn randomMainThread(t: *Threaded, buffer: []u8) void {16552fn randomMainThread(t: *Threaded, buffer: []u8) void {
16554 t.mutex.lock();16553 mutexLockUncancelable(&t.mutex);
16555 defer t.mutex.unlock();16554 defer mutexUnlock(&t.mutex);
1655616555
16557 if (!t.csprng.isInitialized()) {16556 if (!t.csprng.isInitialized()) {
16558 @branchHint(.unlikely);16557 @branchHint(.unlikely);
16559 var seed: [Csprng.seed_len]u8 = undefined;16558 var seed: [Csprng.seed_len]u8 = undefined;
16560 {16559 {
16561 t.mutex.unlock();16560 mutexUnlock(&t.mutex);
16562 defer t.mutex.lock();16561 defer mutexLockUncancelable(&t.mutex);
1656316562
16564 const prev = swapCancelProtection(t, .blocked);16563 const prev = swapCancelProtection(t, .blocked);
16565 defer _ = swapCancelProtection(t, prev);16564 defer _ = swapCancelProtection(t, prev);
...@@ -16744,8 +16743,8 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {...@@ -16744,8 +16743,8 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
1674416743
16745fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {16744fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
16746 {16745 {
16747 t.mutex.lock();16746 mutexLockUncancelable(&t.mutex);
16748 defer t.mutex.unlock();16747 defer mutexUnlock(&t.mutex);
1674916748
16750 if (t.random_file.fd == -2) return error.EntropyUnavailable;16749 if (t.random_file.fd == -2) return error.EntropyUnavailable;
16751 if (t.random_file.fd != -1) return t.random_file.fd;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,8 +16784,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
16785 .SUCCESS => {16784 .SUCCESS => {
16786 syscall.finish();16785 syscall.finish();
16787 if (!statx.mask.TYPE) return error.EntropyUnavailable;16786 if (!statx.mask.TYPE) return error.EntropyUnavailable;
16788 t.mutex.lock(); // Another thread might have won the race.16787 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
16789 defer t.mutex.unlock();16788 defer mutexUnlock(&t.mutex);
16790 if (t.random_file.fd >= 0) {16789 if (t.random_file.fd >= 0) {
16791 posix.close(fd);16790 posix.close(fd);
16792 return t.random_file.fd;16791 return t.random_file.fd;
...@@ -16813,8 +16812,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {...@@ -16813,8 +16812,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
16813 switch (posix.errno(fstat_sym(fd, &stat))) {16812 switch (posix.errno(fstat_sym(fd, &stat))) {
16814 .SUCCESS => {16813 .SUCCESS => {
16815 syscall.finish();16814 syscall.finish();
16816 t.mutex.lock(); // Another thread might have won the race.16815 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
16817 defer t.mutex.unlock();16816 defer mutexUnlock(&t.mutex);
16818 if (t.random_file.fd >= 0) {16817 if (t.random_file.fd >= 0) {
16819 posix.close(fd);16818 posix.close(fd);
16820 return t.random_file.fd;16819 return t.random_file.fd;
...@@ -16878,13 +16877,13 @@ const parking_futex = struct {...@@ -16878,13 +16877,13 @@ const parking_futex = struct {
16878 /// avoid a race.16877 /// avoid a race.
16879 num_waiters: std.atomic.Value(u32),16878 num_waiters: std.atomic.Value(u32),
16880 /// Protects `waiters`.16879 /// Protects `waiters`.
16881 mutex: std.Thread.Mutex,16880 mutex: Io.Mutex,
16882 waiters: std.DoublyLinkedList,16881 waiters: std.DoublyLinkedList,
1688316882
16884 /// Prevent false sharing between buckets.16883 /// Prevent false sharing between buckets.
16885 _: void align(std.atomic.cache_line) = {},16884 _: void align(std.atomic.cache_line) = {},
1688616885
16887 const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} };16886 const init: Bucket = .{ .num_waiters = .init(0), .mutex = .init, .waiters = .{} };
16888 };16887 };
1688916888
16890 const Waiter = struct {16889 const Waiter = struct {
...@@ -16947,8 +16946,8 @@ const parking_futex = struct {...@@ -16947,8 +16946,8 @@ const parking_futex = struct {
16947 var status_buf: std.atomic.Value(Thread.Status) = undefined;16946 var status_buf: std.atomic.Value(Thread.Status) = undefined;
1694816947
16949 {16948 {
16950 bucket.mutex.lock();16949 mutexLockUncancelable(&bucket.mutex);
16951 defer bucket.mutex.unlock();16950 defer mutexUnlock(&bucket.mutex);
1695216951
16953 _ = bucket.num_waiters.fetchAdd(1, .acquire);16952 _ = bucket.num_waiters.fetchAdd(1, .acquire);
1695416953
...@@ -17017,8 +17016,8 @@ const parking_futex = struct {...@@ -17017,8 +17016,8 @@ const parking_futex = struct {
17017 .parked => {17016 .parked => {
17018 // We saw a timeout and updated our own status from `.parked` to `.none`. It is17017 // We saw a timeout and updated our own status from `.parked` to `.none`. It is
17019 // our responsibility to remove `waiter` from `bucket`.17018 // our responsibility to remove `waiter` from `bucket`.
17020 bucket.mutex.lock();17019 mutexLockUncancelable(&bucket.mutex);
17021 defer bucket.mutex.unlock();17020 defer mutexUnlock(&bucket.mutex);
17022 bucket.waiters.remove(&waiter.node);17021 bucket.waiters.remove(&waiter.node);
17023 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);17022 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
17024 },17023 },
...@@ -17057,8 +17056,8 @@ const parking_futex = struct {...@@ -17057,8 +17056,8 @@ const parking_futex = struct {
17057 // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`.17056 // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`.
17058 var waking_head: ?*std.DoublyLinkedList.Node = null;17057 var waking_head: ?*std.DoublyLinkedList.Node = null;
17059 {17058 {
17060 bucket.mutex.lock();17059 mutexLockUncancelable(&bucket.mutex);
17061 defer bucket.mutex.unlock();17060 defer mutexUnlock(&bucket.mutex);
1706217061
17063 var num_removed: u32 = 0;17062 var num_removed: u32 = 0;
17064 var it = bucket.waiters.first;17063 var it = bucket.waiters.first;
...@@ -17113,8 +17112,8 @@ const parking_futex = struct {...@@ -17113,8 +17112,8 @@ const parking_futex = struct {
1711317112
17114 fn removeCanceledWaiter(waiter: *Waiter) void {17113 fn removeCanceledWaiter(waiter: *Waiter) void {
17115 const bucket = bucketForAddress(waiter.address);17114 const bucket = bucketForAddress(waiter.address);
17116 bucket.mutex.lock();17115 mutexLockUncancelable(&bucket.mutex);
17117 defer bucket.mutex.unlock();17116 defer mutexUnlock(&bucket.mutex);
17118 bucket.waiters.remove(&waiter.node);17117 bucket.waiters.remove(&waiter.node);
17119 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);17118 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
17120 waiter.done.store(true, .release); // potentially invalidates `waiter.*`17119 waiter.done.store(true, .release); // potentially invalidates `waiter.*`
...@@ -18102,3 +18101,123 @@ fn eventSet(event: *Io.Event) void {...@@ -18102,3 +18101,123 @@ fn eventSet(event: *Io.Event) void {
18102 .waiting => Thread.futexWake(@ptrCast(event), std.math.maxInt(u32)),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.
18106fn 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.
18126fn 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.
18146fn 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.
18176fn 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.
18195fn 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.
18214fn 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}