| ... | ... | @@ -3448,6 +3448,16 @@ fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) Hos |
| 3448 | 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 | 3461 | fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { |
| 3452 | 3462 | @branchHint(.cold); |
| 3453 | 3463 | |
| ... | ... | @@ -3467,6 +3477,33 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca |
| 3467 | 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 | 3507 | @compileError("TODO"); |
| 3471 | 3508 | } |
| 3472 | 3509 | |
| ... | ... | @@ -3488,6 +3525,32 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi |
| 3488 | 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 | 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 | 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 | 3619 | @compileError("TODO"); |
| 3536 | 3620 | } |
| 3537 | 3621 | |