authorgravatar for chiovolonit@gmail.comThom Chiovoloni <chiovolonit@gmail.com> 2021-08-13 20:43:42-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-13 22:43:42-05:00
logc2635f9b024081717f01d02ea35aaa670c9e2eb7
tree0867ec9ef89c048949b6612db9d511a9bcdff1bb
parent65208a7fa269497e85aab688f4d15f83568075d0
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Fix darwin ulock usage on macOS (#9545)

* Fix darwin ulock usage on macOS * Fix review comments, and correctly handle timeout overflow on darwin * Apply more review suggestions

2 files changed, 27 insertions(+), 10 deletions(-)

lib/std/Thread/Futex.zig+26-9
......@@ -180,23 +180,36 @@ const DarwinFutex = struct {
180180 const darwin = std.os.darwin;
181181
182182 fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
183 // ulock_wait() uses micro-second timeouts, where 0 = INIFITE or no-timeout
184 var timeout_us: u32 = 0;
185 if (timeout) |timeout_ns| {
186 timeout_us = @intCast(u32, timeout_ns / std.time.ns_per_us);
187 }
188
189183 // Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it:
190184 // https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6
191185 //
192186 // This XNU version appears to correspond to 11.0.1:
193187 // https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html
188 //
189 // ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout
190 // ulock_wait2() uses 64-bit nano-second timeouts (with the same convention)
191 var timeout_ns: u64 = 0;
192 if (timeout) |timeout_value| {
193 // This should be checked by the caller.
194 assert(timeout_value != 0);
195 timeout_ns = timeout_value;
196 }
194197 const addr = @ptrCast(*const c_void, ptr);
195198 const flags = darwin.UL_COMPARE_AND_WAIT | darwin.ULF_NO_ERRNO;
199 // If we're using `__ulock_wait` and `timeout` is too big to fit inside a `u32` count of
200 // micro-seconds (around 70min), we'll request a shorter timeout. This is fine (users
201 // should handle spurious wakeups), but we need to remember that we did so, so that
202 // we don't return `TimedOut` incorrectly. If that happens, we set this variable to
203 // true so that we we know to ignore the ETIMEDOUT result.
204 var timeout_overflowed = false;
196205 const status = blk: {
197206 if (target.os.version_range.semver.max.major >= 11) {
198 break :blk darwin.__ulock_wait2(flags, addr, expect, timeout_us, 0);
207 break :blk darwin.__ulock_wait2(flags, addr, expect, timeout_ns, 0);
199208 } else {
209 const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) catch overflow: {
210 timeout_overflowed = true;
211 break :overflow std.math.maxInt(u32);
212 };
200213 break :blk darwin.__ulock_wait(flags, addr, expect, timeout_us);
201214 }
202215 };
......@@ -204,8 +217,11 @@ const DarwinFutex = struct {
204217 if (status >= 0) return;
205218 switch (-status) {
206219 darwin.EINTR => {},
207 darwin.EFAULT => unreachable,
208 darwin.ETIMEDOUT => return error.TimedOut,
220 // Address of the futex is paged out. This is unlikely, but possible in theory, and
221 // pthread/libdispatch on darwin bother to handle it. In this case we'll return
222 // without waiting, but the caller should retry anyway.
223 darwin.EFAULT => {},
224 darwin.ETIMEDOUT => if (!timeout_overflowed) return error.TimedOut,
209225 else => unreachable,
210226 }
211227 }
......@@ -223,6 +239,7 @@ const DarwinFutex = struct {
223239 if (status >= 0) return;
224240 switch (-status) {
225241 darwin.EINTR => continue, // spurious wake()
242 darwin.EFAULT => continue, // address of the lock was paged out
226243 darwin.ENOENT => return, // nothing was woken up
227244 darwin.EALREADY => unreachable, // only for ULF_WAKE_THREAD
228245 else => unreachable,
lib/std/c/darwin.zig+1-1
......@@ -246,7 +246,7 @@ pub const UL_COMPARE_AND_WAIT64 = 5;
246246pub const UL_COMPARE_AND_WAIT64_SHARED = 6;
247247pub const ULF_WAIT_ADAPTIVE_SPIN = 0x40000;
248248
249pub extern "c" fn __ulock_wait2(op: u32, addr: ?*const c_void, val: u64, timeout_us: u32, val2: u64) c_int;
249pub extern "c" fn __ulock_wait2(op: u32, addr: ?*const c_void, val: u64, timeout_ns: u64, val2: u64) c_int;
250250pub extern "c" fn __ulock_wait(op: u32, addr: ?*const c_void, val: u64, timeout_us: u32) c_int;
251251pub extern "c" fn __ulock_wake(op: u32, addr: ?*const c_void, val: u64) c_int;
252252