authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-15 20:16:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
logbf841bb4ae6d6c7cf9494ccc45282b704b40e7f6
tree26e6c93e52ad70ced81c5d2cdd25eef786b2712e
parent18ec9685fbdac7e2e49cb1d0979d4aeea3775bc8

std.Io.Threaded: implement futexes on darwin


1 files changed, 84 insertions(+), 0 deletions(-)

lib/std/Io/Threaded.zig+84
...@@ -3448,6 +3448,16 @@ fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) Hos...@@ -3448,6 +3448,16 @@ fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) Hos
3448 return .{ .bytes = dest };3448 return .{ .bytes = dest };
3449}3449}
34503450
3451/// Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it:
3452/// https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6
3453///
3454/// This XNU version appears to correspond to 11.0.1:
3455/// https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html
3456///
3457/// ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout
3458/// ulock_wait2() uses 64-bit nano-second timeouts (with the same convention)
3459const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= 11;
3460
3451fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void {3461fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void {
3452 @branchHint(.cold);3462 @branchHint(.cold);
34533463
...@@ -3467,6 +3477,33 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca...@@ -3467,6 +3477,33 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca
3467 return;3477 return;
3468 }3478 }
34693479
3480 if (native_os.isDarwin()) {
3481 const c = std.c;
3482 const flags: c.UL = .{
3483 .op = .COMPARE_AND_WAIT,
3484 .NO_ERRNO = true,
3485 };
3486 try t.checkCancel();
3487 const status = if (darwin_supports_ulock_wait2)
3488 c.__ulock_wait2(flags, ptr, expect, 0, 0)
3489 else
3490 c.__ulock_wait(flags, ptr, expect, 0);
3491
3492 if (status >= 0) return;
3493
3494 if (builtin.mode == .Debug) switch (@as(c.E, @enumFromInt(-status))) {
3495 // Wait was interrupted by the OS or other spurious signalling.
3496 .INTR => {},
3497 // Address of the futex was paged out. This is unlikely, but possible in theory, and
3498 // pthread/libdispatch on darwin bother to handle it. In this case we'll return
3499 // without waiting, but the caller should retry anyway.
3500 .FAULT => {},
3501 .TIMEDOUT => unreachable,
3502 else => unreachable,
3503 };
3504 return;
3505 }
3506
3470 @compileError("TODO");3507 @compileError("TODO");
3471}3508}
34723509
...@@ -3488,6 +3525,32 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi...@@ -3488,6 +3525,32 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi
3488 return;3525 return;
3489 }3526 }
34903527
3528 if (native_os.isDarwin()) {
3529 const c = std.c;
3530 const flags: c.UL = .{
3531 .op = .COMPARE_AND_WAIT,
3532 .NO_ERRNO = true,
3533 };
3534 const status = if (darwin_supports_ulock_wait2)
3535 c.__ulock_wait2(flags, ptr, expect, 0, 0)
3536 else
3537 c.__ulock_wait(flags, ptr, expect, 0);
3538
3539 if (status >= 0) return;
3540
3541 if (builtin.mode == .Debug) switch (@as(c.E, @enumFromInt(-status))) {
3542 // Wait was interrupted by the OS or other spurious signalling.
3543 .INTR => {},
3544 // Address of the futex was paged out. This is unlikely, but possible in theory, and
3545 // pthread/libdispatch on darwin bother to handle it. In this case we'll return
3546 // without waiting, but the caller should retry anyway.
3547 .FAULT => {},
3548 .TIMEDOUT => unreachable,
3549 else => unreachable,
3550 };
3551 return;
3552 }
3553
3491 @compileError("TODO");3554 @compileError("TODO");
3492}3555}
34933556
...@@ -3532,6 +3595,27 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void {...@@ -3532,6 +3595,27 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void {
3532 return;3595 return;
3533 }3596 }
35343597
3598 if (native_os.isDarwin()) {
3599 const c = std.c;
3600 const flags: c.UL = .{
3601 .op = .COMPARE_AND_WAIT,
3602 .NO_ERRNO = true,
3603 .WAKE_ALL = max_waiters > 1,
3604 };
3605 const is_debug = builtin.mode == .Debug;
3606 while (true) {
3607 const status = c.__ulock_wake(flags, ptr, 0);
3608 if (status >= 0) return;
3609 switch (@as(c.E, @enumFromInt(-status))) {
3610 .INTR => continue, // spurious wake()
3611 .FAULT => assert(!is_debug), // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t
3612 .NOENT => return, // nothing was woken up
3613 .ALREADY => assert(!is_debug), // only for UL.Op.WAKE_THREAD
3614 else => assert(!is_debug),
3615 }
3616 }
3617 }
3618
3535 @compileError("TODO");3619 @compileError("TODO");
3536}3620}
35373621