| ... | @@ -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 | } |
| 3450 | | 3450 | |
| | 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) |
| | 3459 | const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= 11; |
| | 3460 | |
| 3451 | fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { | 3461 | fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { |
| 3452 | @branchHint(.cold); | 3462 | @branchHint(.cold); |
| 3453 | | 3463 | |
| ... | @@ -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 | } |
| 3469 | | 3479 | |
| | 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 | } |
| 3472 | | 3509 | |
| ... | @@ -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 | } |
| 3490 | | 3527 | |
| | 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 | } |
| 3493 | | 3556 | |
| ... | @@ -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 | } |
| 3534 | | 3597 | |
| | 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 | } |
| 3537 | | 3621 | |