authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-03 19:42:54+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-03 22:38:28+00:00
log56a43fb86f5127171709033fb43e19bc3b8a1cdc
tree44a578b94baa0e187c765ba5b6ea81dae3431311
parent184c8f9545944333bf122203c8d813785b43df8a
signaturelock-open Commit is signed but in an unrecognized format.

Revert "std.Io.Threaded: work around parking futex bug"

This reverts commit 5312063138e787a09493e5f5affb5c8652b66dbc.

1 files changed, 64 insertions(+), 91 deletions(-)

lib/std/Io/Threaded.zig+64-91
......@@ -29,8 +29,8 @@ const ws2_32 = std.os.windows.ws2_32;
2929/// * scanning environment variables on some targets
3030/// * memory-mapping when mmap or equivalent is not available
3131allocator: Allocator,
32mutex: Mutex = .init,
33cond: Condition = .init,
32mutex: Io.Mutex = .init,
33cond: Io.Condition = .init,
3434run_queue: std.SinglyLinkedList = .{},
3535join_requested: bool = false,
3636stack_size: usize,
......@@ -1505,8 +1505,8 @@ var global_single_threaded_instance: Threaded = .init_single_threaded;
15051505pub const global_single_threaded: *Threaded = &global_single_threaded_instance;
15061506
15071507pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
1508 mutexLockInternal(&t.mutex);
1509 defer mutexUnlockInternal(&t.mutex);
1508 mutexLock(&t.mutex);
1509 defer mutexUnlock(&t.mutex);
15101510 t.async_limit = new_limit;
15111511}
15121512
......@@ -1527,8 +1527,8 @@ pub fn deinit(t: *Threaded) void {
15271527fn join(t: *Threaded) void {
15281528 if (builtin.single_threaded) return;
15291529 {
1530 mutexLockInternal(&t.mutex);
1531 defer mutexUnlockInternal(&t.mutex);
1530 mutexLock(&t.mutex);
1531 defer mutexUnlock(&t.mutex);
15321532 t.join_requested = true;
15331533 }
15341534 condBroadcast(&t.cond);
......@@ -1593,16 +1593,16 @@ fn worker(t: *Threaded) void {
15931593
15941594 defer t.wait_group.finish();
15951595
1596 mutexLockInternal(&t.mutex);
1597 defer mutexUnlockInternal(&t.mutex);
1596 mutexLock(&t.mutex);
1597 defer mutexUnlock(&t.mutex);
15981598
15991599 while (true) {
16001600 while (t.run_queue.popFirst()) |runnable_node| {
1601 mutexUnlockInternal(&t.mutex);
1601 mutexUnlock(&t.mutex);
16021602 thread.cancel_protection = .unblocked;
16031603 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);
16041604 runnable.startFn(runnable, &thread, t);
1605 mutexLockInternal(&t.mutex);
1605 mutexLock(&t.mutex);
16061606 t.busy_count -= 1;
16071607 }
16081608 if (t.join_requested) break;
......@@ -2025,12 +2025,12 @@ fn async(
20252025 },
20262026 };
20272027
2028 mutexLockInternal(&t.mutex);
2028 mutexLock(&t.mutex);
20292029
20302030 const busy_count = t.busy_count;
20312031
20322032 if (busy_count >= @intFromEnum(t.async_limit)) {
2033 mutexUnlockInternal(&t.mutex);
2033 mutexUnlock(&t.mutex);
20342034 future.destroy(gpa);
20352035 start(context.ptr, result.ptr);
20362036 return null;
......@@ -2044,7 +2044,7 @@ fn async(
20442044 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
20452045 t.wait_group.finish();
20462046 t.busy_count = busy_count;
2047 mutexUnlockInternal(&t.mutex);
2047 mutexUnlock(&t.mutex);
20482048 future.destroy(gpa);
20492049 start(context.ptr, result.ptr);
20502050 return null;
......@@ -2054,7 +2054,7 @@ fn async(
20542054
20552055 t.run_queue.prepend(&future.runnable.node);
20562056
2057 mutexUnlockInternal(&t.mutex);
2057 mutexUnlock(&t.mutex);
20582058 condSignal(&t.cond);
20592059 return @ptrCast(future);
20602060}
......@@ -2077,8 +2077,8 @@ fn concurrent(
20772077 };
20782078 errdefer future.destroy(gpa);
20792079
2080 mutexLockInternal(&t.mutex);
2081 defer mutexUnlockInternal(&t.mutex);
2080 mutexLock(&t.mutex);
2081 defer mutexUnlock(&t.mutex);
20822082
20832083 const busy_count = t.busy_count;
20842084
......@@ -2122,12 +2122,12 @@ fn groupAsync(
21222122 error.OutOfMemory => return groupAsyncEager(start, context.ptr),
21232123 };
21242124
2125 mutexLockInternal(&t.mutex);
2125 mutexLock(&t.mutex);
21262126
21272127 const busy_count = t.busy_count;
21282128
21292129 if (busy_count >= @intFromEnum(t.async_limit)) {
2130 mutexUnlockInternal(&t.mutex);
2130 mutexUnlock(&t.mutex);
21312131 task.destroy(gpa);
21322132 return groupAsyncEager(start, context.ptr);
21332133 }
......@@ -2140,7 +2140,7 @@ fn groupAsync(
21402140 const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
21412141 t.wait_group.finish();
21422142 t.busy_count = busy_count;
2143 mutexUnlockInternal(&t.mutex);
2143 mutexUnlock(&t.mutex);
21442144 task.destroy(gpa);
21452145 return groupAsyncEager(start, context.ptr);
21462146 };
......@@ -2157,7 +2157,7 @@ fn groupAsync(
21572157 }, .monotonic);
21582158 t.run_queue.prepend(&task.runnable.node);
21592159
2160 mutexUnlockInternal(&t.mutex);
2160 mutexUnlock(&t.mutex);
21612161 condSignal(&t.cond);
21622162}
21632163fn groupAsyncEager(
......@@ -2222,8 +2222,8 @@ fn groupConcurrent(
22222222 };
22232223 errdefer task.destroy(gpa);
22242224
2225 mutexLockInternal(&t.mutex);
2226 defer mutexUnlockInternal(&t.mutex);
2225 mutexLock(&t.mutex);
2226 defer mutexUnlock(&t.mutex);
22272227
22282228 const busy_count = t.busy_count;
22292229
......@@ -3847,8 +3847,8 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
38473847
38483848fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
38493849 if (!t.system_basic_information.initialized.load(.acquire)) {
3850 mutexLockInternal(&t.mutex);
3851 defer mutexUnlockInternal(&t.mutex);
3850 mutexLock(&t.mutex);
3851 defer mutexUnlock(&t.mutex);
38523852
38533853 switch (windows.ntdll.NtQuerySystemInformation(
38543854 .SystemBasicInformation,
......@@ -14359,10 +14359,9 @@ const Wsa = struct {
1435914359};
1436014360
1436114361fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
14362 const t_io = io(t);
1436314362 const wsa = &t.wsa;
14364 try wsa.mutex.lock(t_io);
14365 defer wsa.mutex.unlock(t_io);
14363 try mutexLock(&wsa.mutex);
14364 defer mutexUnlock(&wsa.mutex);
1436614365 switch (wsa.status) {
1436714366 .uninitialized => {
1436814367 var wsa_data: ws2_32.WSADATA = undefined;
......@@ -14433,8 +14432,8 @@ const WindowsEnvironStrings = struct {
1443314432};
1443414433
1443514434fn scanEnviron(t: *Threaded) void {
14436 mutexLockInternal(&t.mutex);
14437 defer mutexUnlockInternal(&t.mutex);
14435 mutexLock(&t.mutex);
14436 defer mutexUnlock(&t.mutex);
1443814437
1443914438 if (t.environ.initialized) return;
1444014439 t.environ.initialized = true;
......@@ -14789,8 +14788,8 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1478914788
1479014789fn getDevNullFd(t: *Threaded) !posix.fd_t {
1479114790 {
14792 mutexLockInternal(&t.mutex);
14793 defer mutexUnlockInternal(&t.mutex);
14791 mutexLock(&t.mutex);
14792 defer mutexUnlock(&t.mutex);
1479414793 if (t.null_file.fd != -1) return t.null_file.fd;
1479514794 }
1479614795 const mode: u32 = 0;
......@@ -14801,8 +14800,8 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
1480114800 .SUCCESS => {
1480214801 syscall.finish();
1480314802 const fresh_fd: posix.fd_t = @intCast(rc);
14804 mutexLockInternal(&t.mutex); // Another thread might have won the race.
14805 defer mutexUnlockInternal(&t.mutex);
14803 mutexLock(&t.mutex); // Another thread might have won the race.
14804 defer mutexUnlock(&t.mutex);
1480614805 if (t.null_file.fd != -1) {
1480714806 posix.close(fresh_fd);
1480814807 return t.null_file.fd;
......@@ -15462,8 +15461,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1546215461
1546315462fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1546415463 {
15465 mutexLockInternal(&t.mutex);
15466 defer mutexUnlockInternal(&t.mutex);
15464 mutexLock(&t.mutex);
15465 defer mutexUnlock(&t.mutex);
1546715466 if (t.random_file.handle) |handle| return handle;
1546815467 }
1546915468
......@@ -15497,8 +15496,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1549715496 )) {
1549815497 .SUCCESS => {
1549915498 syscall.finish();
15500 mutexLockInternal(&t.mutex); // Another thread might have won the race.
15501 defer mutexUnlockInternal(&t.mutex);
15499 mutexLock(&t.mutex); // Another thread might have won the race.
15500 defer mutexUnlock(&t.mutex);
1550215501 if (t.random_file.handle) |prev_handle| {
1550315502 windows.CloseHandle(fresh_handle);
1550415503 return prev_handle;
......@@ -15518,8 +15517,8 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1551815517
1551915518fn getNulHandle(t: *Threaded) !windows.HANDLE {
1552015519 {
15521 mutexLockInternal(&t.mutex);
15522 defer mutexUnlockInternal(&t.mutex);
15520 mutexLock(&t.mutex);
15521 defer mutexUnlock(&t.mutex);
1552315522 if (t.null_file.handle) |handle| return handle;
1552415523 }
1552515524
......@@ -15565,8 +15564,8 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE {
1556515564 )) {
1556615565 .SUCCESS => {
1556715566 syscall.finish();
15568 mutexLockInternal(&t.mutex); // Another thread might have won the race.
15569 defer mutexUnlockInternal(&t.mutex);
15567 mutexLock(&t.mutex); // Another thread might have won the race.
15568 defer mutexUnlock(&t.mutex);
1557015569 if (t.null_file.handle) |prev_handle| {
1557115570 windows.CloseHandle(fresh_handle);
1557215571 return prev_handle;
......@@ -16611,15 +16610,15 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void {
1661116610}
1661216611
1661316612fn randomMainThread(t: *Threaded, buffer: []u8) void {
16614 mutexLockInternal(&t.mutex);
16615 defer mutexUnlockInternal(&t.mutex);
16613 mutexLock(&t.mutex);
16614 defer mutexUnlock(&t.mutex);
1661616615
1661716616 if (!t.csprng.isInitialized()) {
1661816617 @branchHint(.unlikely);
1661916618 var seed: [Csprng.seed_len]u8 = undefined;
1662016619 {
16621 mutexUnlockInternal(&t.mutex);
16622 defer mutexLockInternal(&t.mutex);
16620 mutexUnlock(&t.mutex);
16621 defer mutexLock(&t.mutex);
1662316622
1662416623 const prev = swapCancelProtection(t, .blocked);
1662516624 defer _ = swapCancelProtection(t, prev);
......@@ -16804,8 +16803,8 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
1680416803
1680516804fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1680616805 {
16807 mutexLockInternal(&t.mutex);
16808 defer mutexUnlockInternal(&t.mutex);
16806 mutexLock(&t.mutex);
16807 defer mutexUnlock(&t.mutex);
1680916808
1681016809 if (t.random_file.fd == -2) return error.EntropyUnavailable;
1681116810 if (t.random_file.fd != -1) return t.random_file.fd;
......@@ -16845,8 +16844,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1684516844 .SUCCESS => {
1684616845 syscall.finish();
1684716846 if (!statx.mask.TYPE) return error.EntropyUnavailable;
16848 mutexLockInternal(&t.mutex); // Another thread might have won the race.
16849 defer mutexUnlockInternal(&t.mutex);
16847 mutexLock(&t.mutex); // Another thread might have won the race.
16848 defer mutexUnlock(&t.mutex);
1685016849 if (t.random_file.fd >= 0) {
1685116850 posix.close(fd);
1685216851 return t.random_file.fd;
......@@ -16873,8 +16872,8 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1687316872 switch (posix.errno(fstat_sym(fd, &stat))) {
1687416873 .SUCCESS => {
1687516874 syscall.finish();
16876 mutexLockInternal(&t.mutex); // Another thread might have won the race.
16877 defer mutexUnlockInternal(&t.mutex);
16875 mutexLock(&t.mutex); // Another thread might have won the race.
16876 defer mutexUnlock(&t.mutex);
1687816877 if (t.random_file.fd >= 0) {
1687916878 posix.close(fd);
1688016879 return t.random_file.fd;
......@@ -16938,7 +16937,7 @@ const parking_futex = struct {
1693816937 /// avoid a race.
1693916938 num_waiters: std.atomic.Value(u32),
1694016939 /// Protects `waiters`.
16941 mutex: Mutex,
16940 mutex: Io.Mutex,
1694216941 waiters: std.DoublyLinkedList,
1694316942
1694416943 /// Prevent false sharing between buckets.
......@@ -17007,8 +17006,8 @@ const parking_futex = struct {
1700717006 var status_buf: std.atomic.Value(Thread.Status) = undefined;
1700817007
1700917008 {
17010 mutexLockInternal(&bucket.mutex);
17011 defer mutexUnlockInternal(&bucket.mutex);
17009 mutexLock(&bucket.mutex);
17010 defer mutexUnlock(&bucket.mutex);
1701217011
1701317012 _ = bucket.num_waiters.fetchAdd(1, .acquire);
1701417013
......@@ -17077,8 +17076,8 @@ const parking_futex = struct {
1707717076 .parked => {
1707817077 // We saw a timeout and updated our own status from `.parked` to `.none`. It is
1707917078 // our responsibility to remove `waiter` from `bucket`.
17080 mutexLockInternal(&bucket.mutex);
17081 defer mutexUnlockInternal(&bucket.mutex);
17079 mutexLock(&bucket.mutex);
17080 defer mutexUnlock(&bucket.mutex);
1708217081 bucket.waiters.remove(&waiter.node);
1708317082 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
1708417083 },
......@@ -17117,8 +17116,8 @@ const parking_futex = struct {
1711717116 // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`.
1711817117 var waking_head: ?*std.DoublyLinkedList.Node = null;
1711917118 {
17120 mutexLockInternal(&bucket.mutex);
17121 defer mutexUnlockInternal(&bucket.mutex);
17119 mutexLock(&bucket.mutex);
17120 defer mutexUnlock(&bucket.mutex);
1712217121
1712317122 var num_removed: u32 = 0;
1712417123 var it = bucket.waiters.first;
......@@ -17173,8 +17172,8 @@ const parking_futex = struct {
1717317172
1717417173 fn removeCanceledWaiter(waiter: *Waiter) void {
1717517174 const bucket = bucketForAddress(waiter.address);
17176 mutexLockInternal(&bucket.mutex);
17177 defer mutexUnlockInternal(&bucket.mutex);
17175 mutexLock(&bucket.mutex);
17176 defer mutexUnlock(&bucket.mutex);
1717817177 bucket.waiters.remove(&waiter.node);
1717917178 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
1718017179 waiter.done.store(true, .release); // potentially invalidates `waiter.*`
......@@ -18160,14 +18159,8 @@ fn eventSet(event: *Io.Event) void {
1816018159 }
1816118160}
1816218161
18163const Condition = if (!is_windows) Io.Condition else struct {
18164 condition: windows.CONDITION_VARIABLE,
18165 const init: @This() = .{ .condition = .{} };
18166};
18167
1816818162/// Same as `Io.Condition.broadcast` but avoids the VTable.
18169fn condBroadcast(cond: *Condition) void {
18170 if (is_windows) return windows.ntdll.RtlWakeAllConditionVariable(&cond.condition);
18163fn condBroadcast(cond: *Io.Condition) void {
1817118164 var prev_state = cond.state.load(.monotonic);
1817218165 while (prev_state.waiters > prev_state.signals) {
1817318166 @branchHint(.unlikely);
......@@ -18187,8 +18180,7 @@ fn condBroadcast(cond: *Condition) void {
1818718180}
1818818181
1818918182/// Same as `Io.Condition.signal` but avoids the VTable.
18190fn condSignal(cond: *Condition) void {
18191 if (is_windows) return windows.ntdll.RtlWakeConditionVariable(&cond.condition);
18183fn condSignal(cond: *Io.Condition) void {
1819218184 var prev_state = cond.state.load(.monotonic);
1819318185 while (prev_state.waiters > prev_state.signals) {
1819418186 @branchHint(.unlikely);
......@@ -18208,11 +18200,7 @@ fn condSignal(cond: *Condition) void {
1820818200}
1820918201
1821018202/// Same as `Io.Condition.waitUncancelable` but avoids the VTable.
18211fn condWait(cond: *Condition, mutex: *Mutex) void {
18212 if (is_windows) {
18213 _ = windows.kernel32.SleepConditionVariableSRW(&cond.condition, &mutex.srwlock, windows.INFINITE, 0);
18214 return;
18215 }
18203fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {
1821618204 var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load
1821718205
1821818206 {
......@@ -18220,8 +18208,8 @@ fn condWait(cond: *Condition, mutex: *Mutex) void {
1822018208 assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters
1822118209 }
1822218210
18223 mutexUnlockInternal(mutex);
18224 defer mutexLockInternal(mutex);
18211 mutexUnlock(mutex);
18212 defer mutexLock(mutex);
1822518213
1822618214 while (true) {
1822718215 Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null);
......@@ -18241,16 +18229,6 @@ fn condWait(cond: *Condition, mutex: *Mutex) void {
1824118229 }
1824218230}
1824318231
18244const Mutex = if (!is_windows) Io.Mutex else struct {
18245 srwlock: windows.SRWLOCK,
18246 const init: @This() = .{ .srwlock = .{} };
18247};
18248
18249fn mutexLockInternal(m: *Mutex) void {
18250 if (is_windows) return windows.ntdll.RtlAcquireSRWLockExclusive(&m.srwlock);
18251 return mutexLock(m);
18252}
18253
1825418232/// Same as `Io.Mutex.lockUncancelable` but avoids the VTable.
1825518233pub fn mutexLock(m: *Io.Mutex) void {
1825618234 const initial_state = m.state.cmpxchgWeak(
......@@ -18270,11 +18248,6 @@ pub fn mutexLock(m: *Io.Mutex) void {
1827018248 }
1827118249}
1827218250
18273fn mutexUnlockInternal(m: *Mutex) void {
18274 if (is_windows) return windows.ntdll.RtlReleaseSRWLockExclusive(&m.srwlock);
18275 return mutexUnlock(m);
18276}
18277
1827818251/// Same as `Io.Mutex.unlock` but avoids the VTable.
1827918252pub fn mutexUnlock(m: *Io.Mutex) void {
1828018253 switch (m.state.swap(.unlocked, .release)) {