authorgravatar for 45520026+kprotty@users.noreply.github.comprotty <45520026+kprotty@users.noreply.github.com> 2021-06-12 08:51:37-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-12 08:51:37-05:00
log2ba68f9f4c1de8677db35851cf9e019be132d65b
tree1f5922ff281300c594bc18a0a2e964c94cfd59c7
parent2b2efa24d08551bdb1ea58a39429bf2a5493b1b4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.Thread.Futex addition (#9070)

* std.Thread.Futex: implementation + tests * std.Thread.Futex: fix darwin compile errors * std.Thread.Futex: fix wait() documentation typo * std.Thread.Futex: fix darwin version check * std.Thread.Futex: remove unnecessary comptime keyword

6 files changed, 654 insertions(+), 11 deletions(-)

CMakeLists.txt+1
......@@ -520,6 +520,7 @@ set(ZIG_STAGE2_SOURCES
520520 "${CMAKE_SOURCE_DIR}/lib/std/target/x86.zig"
521521 "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig"
522522 "${CMAKE_SOURCE_DIR}/lib/std/Thread/AutoResetEvent.zig"
523 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig"
523524 "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig"
524525 "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig"
525526 "${CMAKE_SOURCE_DIR}/lib/std/Thread/StaticResetEvent.zig"
lib/std/Thread.zig+2
......@@ -11,6 +11,7 @@
1111data: Data,
1212
1313pub const AutoResetEvent = @import("Thread/AutoResetEvent.zig");
14pub const Futex = @import("Thread/Futex.zig");
1415pub const ResetEvent = @import("Thread/ResetEvent.zig");
1516pub const StaticResetEvent = @import("Thread/StaticResetEvent.zig");
1617pub const Mutex = @import("Thread/Mutex.zig");
......@@ -574,6 +575,7 @@ pub fn getCurrentThreadId() u64 {
574575test "std.Thread" {
575576 if (!builtin.single_threaded) {
576577 _ = AutoResetEvent;
578 _ = Futex;
577579 _ = ResetEvent;
578580 _ = StaticResetEvent;
579581 _ = Mutex;
lib/std/Thread/Futex.zig created+570
......@@ -0,0 +1,570 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints.
8//! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value.
9//! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`.
10//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals.
11
12const std = @import("../std.zig");
13const Futex = @This();
14
15const target = std.Target.current;
16const single_threaded = std.builtin.single_threaded;
17
18const assert = std.debug.assert;
19const testing = std.testing;
20
21const Atomic = std.atomic.Atomic;
22const spinLoopHint = std.atomic.spinLoopHint;
23
24/// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either:
25/// - The value at `ptr` is no longer equal to `expect`.
26/// - The caller is unblocked by a matching `wake()`.
27/// - The caller is unblocked spuriously by an arbitrary internal signal.
28///
29/// If `timeout` is provided, and the caller is blocked for longer than `timeout` nanoseconds`, `error.TimedOut` is returned.
30///
31/// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically
32/// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`.
33pub fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
34 if (single_threaded) {
35 // check whether the caller should block
36 if (ptr.loadUnchecked() != expect) {
37 return;
38 }
39
40 // There are no other threads which could notify the caller on single_threaded.
41 // Therefor a wait() without a timeout would block indefinitely.
42 const timeout_ns = timeout orelse {
43 @panic("deadlock");
44 };
45
46 // Simulate blocking with the timeout knowing that:
47 // - no other thread can change the ptr value
48 // - no other thread could unblock us if we waiting on the ptr
49 std.time.sleep(timeout_ns);
50 return error.TimedOut;
51 }
52
53 // Avoid calling into the OS for no-op waits()
54 if (timeout) |timeout_ns| {
55 if (timeout_ns == 0) {
56 if (ptr.load(.SeqCst) != expect) return;
57 return error.TimedOut;
58 }
59 }
60
61 return OsFutex.wait(ptr, expect, timeout);
62}
63
64/// Unblocks at most `num_waiters` callers blocked in a `wait()` call on `ptr`.
65/// `num_waiters` of 1 unblocks at most one `wait(ptr, ...)` and `maxInt(u32)` unblocks effectively all `wait(ptr, ...)`.
66pub fn wake(ptr: *const Atomic(u32), num_waiters: u32) void {
67 if (num_waiters == 0 or single_threaded) {
68 return;
69 }
70
71 return OsFutex.wake(ptr, num_waiters);
72}
73
74const OsFutex = if (target.os.tag == .windows)
75 WindowsFutex
76else if (target.os.tag == .linux)
77 LinuxFutex
78else if (target.isDarwin())
79 DarwinFutex
80else if (std.builtin.link_libc)
81 PosixFutex
82else
83 @compileError("Operating System unsupported");
84
85const WindowsFutex = struct {
86 const windows = std.os.windows;
87
88 fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
89 var timeout_value: windows.LARGE_INTEGER = undefined;
90 var timeout_ptr: ?*const windows.LARGE_INTEGER = null;
91
92 // NTDLL functions work with time in units of 100 nanoseconds.
93 // Positive values for timeouts are absolute time while negative is relative.
94 if (timeout) |timeout_ns| {
95 timeout_ptr = &timeout_value;
96 timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100);
97 }
98
99 switch (windows.ntdll.RtlWaitOnAddress(
100 @ptrCast(?*const c_void, ptr),
101 @ptrCast(?*const c_void, &expect),
102 @sizeOf(@TypeOf(expect)),
103 timeout_ptr,
104 )) {
105 .SUCCESS => {},
106 .TIMEOUT => return error.TimedOut,
107 else => unreachable,
108 }
109 }
110
111 fn wake(ptr: *const Atomic(u32), num_waiters: u32) void {
112 const address = @ptrCast(?*const c_void, ptr);
113 switch (num_waiters) {
114 1 => windows.ntdll.RtlWakeAddressSingle(address),
115 else => windows.ntdll.RtlWakeAddressAll(address),
116 }
117 }
118};
119
120const LinuxFutex = struct {
121 const linux = std.os.linux;
122
123 fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
124 var ts: std.os.timespec = undefined;
125 var ts_ptr: ?*std.os.timespec = null;
126
127 // Futex timespec timeout is already in relative time.
128 if (timeout) |timeout_ns| {
129 ts_ptr = &ts;
130 ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s);
131 ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s);
132 }
133
134 switch (linux.getErrno(linux.futex_wait(
135 @ptrCast(*const i32, ptr),
136 linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
137 @bitCast(i32, expect),
138 ts_ptr,
139 ))) {
140 0 => {}, // notified by `wake()`
141 std.os.EINTR => {}, // spurious wakeup
142 std.os.EAGAIN => {}, // ptr.* != expect
143 std.os.ETIMEDOUT => return error.TimedOut,
144 std.os.EINVAL => {}, // possibly timeout overflow
145 std.os.EFAULT => unreachable,
146 else => unreachable,
147 }
148 }
149
150 fn wake(ptr: *const Atomic(u32), num_waiters: u32) void {
151 switch (linux.getErrno(linux.futex_wake(
152 @ptrCast(*const i32, ptr),
153 linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
154 std.math.cast(i32, num_waiters) catch std.math.maxInt(i32),
155 ))) {
156 0 => {}, // successful wake up
157 std.os.EINVAL => {}, // invalid futex_wait() on ptr done elsewhere
158 std.os.EFAULT => {}, // pointer became invalid while doing the wake
159 else => unreachable,
160 }
161 }
162};
163
164const DarwinFutex = struct {
165 const darwin = std.os.darwin;
166
167 fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
168 // ulock_wait() uses micro-second timeouts, where 0 = INIFITE or no-timeout
169 var timeout_us: u32 = 0;
170 if (timeout) |timeout_ns| {
171 timeout_us = @intCast(u32, timeout_ns / std.time.ns_per_us);
172 }
173
174 // Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it:
175 // https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6
176 //
177 // This XNU version appears to correspond to 11.0.1:
178 // https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html
179 const addr = @ptrCast(*const c_void, ptr);
180 const flags = darwin.UL_COMPARE_AND_WAIT | darwin.ULF_NO_ERRNO;
181 const status = blk: {
182 if (target.os.version_range.semver.max.major >= 11) {
183 break :blk darwin.__ulock_wait2(flags, addr, expect, timeout_us, 0);
184 } else {
185 break :blk darwin.__ulock_wait(flags, addr, expect, timeout_us);
186 }
187 };
188
189 if (status >= 0) return;
190 switch (-status) {
191 darwin.EINTR => {},
192 darwin.EFAULT => unreachable,
193 darwin.ETIMEDOUT => return error.TimedOut,
194 else => unreachable,
195 }
196 }
197
198 fn wake(ptr: *const Atomic(u32), num_waiters: u32) void {
199 var flags: u32 = darwin.UL_COMPARE_AND_WAIT | darwin.ULF_NO_ERRNO;
200 if (num_waiters > 1) {
201 flags |= darwin.ULF_WAKE_ALL;
202 }
203
204 while (true) {
205 const addr = @ptrCast(*const c_void, ptr);
206 const status = darwin.__ulock_wake(flags, addr, 0);
207
208 if (status >= 0) return;
209 switch (-status) {
210 darwin.EINTR => continue, // spurious wake()
211 darwin.ENOENT => return, // nothing was woken up
212 darwin.EALREADY => unreachable, // only for ULF_WAKE_THREAD
213 else => unreachable,
214 }
215 }
216 }
217};
218
219const PosixFutex = struct {
220 fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void {
221 const address = @ptrToInt(ptr);
222 const bucket = Bucket.from(address);
223 var waiter: List.Node = undefined;
224
225 {
226 assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
227 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
228
229 if (ptr.load(.SeqCst) != expect) {
230 return;
231 }
232
233 waiter.data = .{ .address = address };
234 bucket.list.prepend(&waiter);
235 }
236
237 var timed_out = false;
238 waiter.data.wait(timeout) catch {
239 defer if (!timed_out) {
240 waiter.data.wait(null) catch unreachable;
241 };
242
243 assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
244 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
245
246 if (waiter.data.address == address) {
247 timed_out = true;
248 bucket.list.remove(&waiter);
249 }
250 };
251
252 waiter.data.deinit();
253 if (timed_out) {
254 return error.TimedOut;
255 }
256 }
257
258 fn wake(ptr: *const Atomic(u32), num_waiters: u32) void {
259 const address = @ptrToInt(ptr);
260 const bucket = Bucket.from(address);
261 var can_notify = num_waiters;
262
263 var notified = List{};
264 defer while (notified.popFirst()) |waiter| {
265 waiter.data.notify();
266 };
267
268 assert(std.c.pthread_mutex_lock(&bucket.mutex) == 0);
269 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == 0);
270
271 var waiters = bucket.list.first;
272 while (waiters) |waiter| {
273 assert(waiter.data.address != null);
274 waiters = waiter.next;
275
276 if (waiter.data.address != address) continue;
277 if (can_notify == 0) break;
278 can_notify -= 1;
279
280 bucket.list.remove(waiter);
281 waiter.data.address = null;
282 notified.prepend(waiter);
283 }
284 }
285
286 const Bucket = struct {
287 mutex: std.c.pthread_mutex_t = .{},
288 list: List = .{},
289
290 var buckets = [_]Bucket{.{}} ** 64;
291
292 fn from(address: usize) *Bucket {
293 return &buckets[address % buckets.len];
294 }
295 };
296
297 const List = std.TailQueue(struct {
298 address: ?usize,
299 state: State = .empty,
300 cond: std.c.pthread_cond_t = .{},
301 mutex: std.c.pthread_mutex_t = .{},
302
303 const Self = @This();
304 const State = enum {
305 empty,
306 waiting,
307 notified,
308 };
309
310 fn deinit(self: *Self) void {
311 const rc = std.c.pthread_cond_destroy(&self.cond);
312 assert(rc == 0 or rc == std.os.EINVAL);
313
314 const rm = std.c.pthread_mutex_destroy(&self.mutex);
315 assert(rm == 0 or rm == std.os.EINVAL);
316 }
317
318 fn wait(self: *Self, timeout: ?u64) error{TimedOut}!void {
319 assert(std.c.pthread_mutex_lock(&self.mutex) == 0);
320 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == 0);
321
322 switch (self.state) {
323 .empty => self.state = .waiting,
324 .waiting => unreachable,
325 .notified => return,
326 }
327
328 var ts: std.os.timespec = undefined;
329 var ts_ptr: ?*const std.os.timespec = null;
330 if (timeout) |timeout_ns| {
331 ts_ptr = &ts;
332 std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts) catch unreachable;
333 ts.tv_sec += @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s);
334 ts.tv_nsec += @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s);
335 if (ts.tv_nsec >= std.time.ns_per_s) {
336 ts.tv_sec += 1;
337 ts.tv_nsec -= std.time.ns_per_s;
338 }
339 }
340
341 while (true) {
342 switch (self.state) {
343 .empty => unreachable,
344 .waiting => {},
345 .notified => return,
346 }
347
348 const ts_ref = ts_ptr orelse {
349 assert(std.c.pthread_cond_wait(&self.cond, &self.mutex) == 0);
350 continue;
351 };
352
353 const rc = std.c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ref);
354 assert(rc == 0 or rc == std.os.ETIMEDOUT);
355 if (rc == std.os.ETIMEDOUT) {
356 self.state = .empty;
357 return error.TimedOut;
358 }
359 }
360 }
361
362 fn notify(self: *Self) void {
363 assert(std.c.pthread_mutex_lock(&self.mutex) == 0);
364 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == 0);
365
366 switch (self.state) {
367 .empty => self.state = .notified,
368 .waiting => {
369 self.state = .notified;
370 assert(std.c.pthread_cond_signal(&self.cond) == 0);
371 },
372 .notified => unreachable,
373 }
374 }
375 });
376};
377
378test "Futex - wait/wake" {
379 var value = Atomic(u32).init(0);
380 Futex.wait(&value, 1, null) catch unreachable;
381
382 const wait_noop_result = Futex.wait(&value, 0, 0);
383 try testing.expectError(error.TimedOut, wait_noop_result);
384
385 const wait_longer_result = Futex.wait(&value, 0, std.time.ns_per_ms);
386 try testing.expectError(error.TimedOut, wait_longer_result);
387
388 Futex.wake(&value, 0);
389 Futex.wake(&value, 1);
390 Futex.wake(&value, std.math.maxInt(u32));
391}
392
393test "Futex - Signal" {
394 if (!single_threaded) {
395 return;
396 }
397
398 try (struct {
399 value: Atomic(u32) = Atomic(u32).init(0),
400
401 const Self = @This();
402
403 fn send(self: *Self, value: u32) void {
404 self.value.store(value, .Release);
405 Futex.wake(&self.value, 1);
406 }
407
408 fn recv(self: *Self, expected: u32) void {
409 while (true) {
410 const value = self.value.load(.Acquire);
411 if (value == expected) break;
412 Futex.wait(&self.value, value, null) catch unreachable;
413 }
414 }
415
416 const Thread = struct {
417 tx: *Self,
418 rx: *Self,
419
420 const start_value = 1;
421
422 fn run(self: Thread) void {
423 var iterations: u32 = start_value;
424 while (iterations < 10) : (iterations += 1) {
425 self.rx.recv(iterations);
426 self.tx.send(iterations);
427 }
428 }
429 };
430
431 fn run() !void {
432 var ping = Self{};
433 var pong = Self{};
434
435 const t1 = try std.Thread.spawn(Thread.run, .{ .rx = &ping, .tx = &pong });
436 defer t1.wait();
437
438 const t2 = try std.Thread.spawn(Thread.run, .{ .rx = &pong, .tx = &ping });
439 defer t2.wait();
440
441 ping.send(Thread.start_value);
442 }
443 }).run();
444}
445
446test "Futex - Broadcast" {
447 if (!single_threaded) {
448 return;
449 }
450
451 try (struct {
452 threads: [10]*std.Thread = undefined,
453 broadcast: Atomic(u32) = Atomic(u32).init(0),
454 notified: Atomic(usize) = Atomic(usize).init(0),
455
456 const Self = @This();
457
458 const BROADCAST_EMPTY = 0;
459 const BROADCAST_SENT = 1;
460 const BROADCAST_RECEIVED = 2;
461
462 fn runReceiver(self: *Self) void {
463 while (true) {
464 const broadcast = self.broadcast.load(.Acquire);
465 if (broadcast == BROADCAST_SENT) break;
466 assert(broadcast == BROADCAST_EMPTY);
467 Futex.wait(&self.broadcast, broadcast, null) catch unreachable;
468 }
469
470 const notified = self.notified.fetchAdd(1, .Monotonic);
471 if (notified + 1 == self.threads.len) {
472 self.broadcast.store(BROADCAST_RECEIVED, .Release);
473 Futex.wake(&self.broadcast, 1);
474 }
475 }
476
477 fn run() !void {
478 var self = Self{};
479
480 for (self.threads) |*thread|
481 thread.* = try std.Thread.spawn(runReceiver, &self);
482 defer for (self.threads) |thread|
483 thread.wait();
484
485 std.time.sleep(16 * std.time.ns_per_ms);
486 self.broadcast.store(BROADCAST_SENT, .Monotonic);
487 Futex.wake(&self.broadcast, @intCast(u32, self.threads.len));
488
489 while (true) {
490 const broadcast = self.broadcast.load(.Acquire);
491 if (broadcast == BROADCAST_RECEIVED) break;
492 try testing.expectEqual(broadcast, BROADCAST_SENT);
493 Futex.wait(&self.broadcast, broadcast, null) catch unreachable;
494 }
495
496 const notified = self.notified.load(.Monotonic);
497 try testing.expectEqual(notified, self.threads.len);
498 }
499 }).run();
500}
501
502test "Futex - Chain" {
503 if (!single_threaded) {
504 return;
505 }
506
507 try (struct {
508 completed: Signal = .{},
509 threads: [10]struct {
510 thread: *std.Thread,
511 signal: Signal,
512 } = undefined,
513
514 const Signal = struct {
515 state: Atomic(u32) = Atomic(u32).init(0),
516
517 fn wait(self: *Signal) void {
518 while (true) {
519 const value = self.value.load(.Acquire);
520 if (value == 1) break;
521 assert(value == 0);
522 Futex.wait(&self.value, 0, null) catch unreachable;
523 }
524 }
525
526 fn notify(self: *Signal) void {
527 assert(self.value.load(.Unordered) == 0);
528 self.value.store(1, .Release);
529 Futex.wake(&self.value, 1);
530 }
531 };
532
533 const Self = @This();
534 const Chain = struct {
535 self: *Self,
536 index: usize,
537
538 fn run(chain: Chain) void {
539 const this_signal = &chain.self.threads[chain.index].signal;
540
541 var next_signal = &chain.self.completed;
542 if (chain.index + 1 < chain.self.threads.len) {
543 next_signal = &chain.self.threads[chain.index + 1].signal;
544 }
545
546 this_signal.wait();
547 next_signal.notify();
548 }
549 };
550
551 fn run() !void {
552 var self = Self{};
553
554 for (self.threads) |*entry, index| {
555 entry.signal = .{};
556 entry.thread = try std.Thread.spawn(Chain.run, .{
557 .self = &self,
558 .index = index,
559 });
560 }
561
562 self.threads[0].signal.notify();
563 self.completed.wait();
564
565 for (self.threads) |entry| {
566 entry.thread.wait();
567 }
568 }
569 }).run();
570}
\ No newline at end of file
lib/std/c/darwin.zig+56-4
......@@ -197,13 +197,65 @@ pub extern "c" fn pthread_threadid_np(thread: ?pthread_t, thread_id: *u64) c_int
197197pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
198198
199199// Grand Central Dispatch is exposed by libSystem.
200pub extern "c" fn dispatch_release(object: *c_void) void;
201
200202pub const dispatch_semaphore_t = *opaque {};
201pub const dispatch_time_t = u64;
202pub const DISPATCH_TIME_NOW = @as(dispatch_time_t, 0);
203pub const DISPATCH_TIME_FOREVER = ~@as(dispatch_time_t, 0);
204203pub extern "c" fn dispatch_semaphore_create(value: isize) ?dispatch_semaphore_t;
205204pub extern "c" fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) isize;
206205pub extern "c" fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) isize;
207206
208pub extern "c" fn dispatch_release(object: *c_void) void;
207pub const dispatch_time_t = u64;
208pub const DISPATCH_TIME_NOW = @as(dispatch_time_t, 0);
209pub const DISPATCH_TIME_FOREVER = ~@as(dispatch_time_t, 0);
209210pub extern "c" fn dispatch_time(when: dispatch_time_t, delta: i64) dispatch_time_t;
211
212const dispatch_once_t = usize;
213const dispatch_function_t = fn (?*c_void) callconv(.C) void;
214pub extern fn dispatch_once_f(
215 predicate: *dispatch_once_t,
216 context: ?*c_void,
217 function: dispatch_function_t,
218) void;
219
220// Undocumented futex-like API available on darwin 16+
221// (macOS 10.12+, iOS 10.0+, tvOS 10.0+, watchOS 3.0+, catalyst 13.0+).
222//
223// [ulock.h]: https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ulock.h
224// [sys_ulock.c]: https://github.com/apple/darwin-xnu/blob/master/bsd/kern/sys_ulock.c
225
226pub const UL_COMPARE_AND_WAIT = 1;
227pub const UL_UNFAIR_LOCK = 2;
228
229// Obsolete/deprecated
230pub const UL_OSSPINLOCK = UL_COMPARE_AND_WAIT;
231pub const UL_HANDOFFLOCK = UL_UNFAIR_LOCK;
232
233pub const ULF_WAKE_ALL = 0x100;
234pub const ULF_WAKE_THREAD = 0x200;
235pub const ULF_WAIT_WORKQ_DATA_CONTENTION = 0x10000;
236pub const ULF_WAIT_CANCEL_POINT = 0x20000;
237pub const ULF_NO_ERRNO = 0x1000000;
238
239// The following are only supported on darwin 19+
240// (macOS 10.15+, iOS 13.0+)
241pub const UL_COMPARE_AND_WAIT_SHARED = 3;
242pub const UL_UNFAIR_LOCK64_SHARED = 4;
243pub const UL_COMPARE_AND_WAIT64 = 5;
244pub const UL_COMPARE_AND_WAIT64_SHARED = 6;
245pub const ULF_WAIT_ADAPTIVE_SPIN = 0x40000;
246
247pub extern "c" fn __ulock_wait2(op: u32, addr: ?*const c_void, val: u64, timeout_us: u32, val2: u64) c_int;
248pub extern "c" fn __ulock_wait(op: u32, addr: ?*const c_void, val: u64, timeout_us: u32) c_int;
249pub extern "c" fn __ulock_wake(op: u32, addr: ?*const c_void, val: u64) c_int;
250
251pub const OS_UNFAIR_LOCK_INIT = os_unfair_lock{};
252pub const os_unfair_lock_t = *os_unfair_lock;
253pub const os_unfair_lock = extern struct {
254 _os_unfair_lock_opaque: u32 = 0,
255};
256
257pub extern "c" fn os_unfair_lock_lock(o: os_unfair_lock_t) void;
258pub extern "c" fn os_unfair_lock_unlock(o: os_unfair_lock_t) void;
259pub extern "c" fn os_unfair_lock_trylock(o: os_unfair_lock_t) bool;
260pub extern "c" fn os_unfair_lock_assert_owner(o: os_unfair_lock_t) void;
261pub extern "c" fn os_unfair_lock_assert_not_owner(o: os_unfair_lock_t) void;
\ No newline at end of file
lib/std/os/linux.zig+1-1
......@@ -181,7 +181,7 @@ pub fn fallocate(fd: i32, mode: i32, offset: i64, length: i64) usize {
181181 }
182182}
183183
184pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize {
184pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*const timespec) usize {
185185 return syscall4(.futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout));
186186}
187187
lib/std/os/windows/ntdll.zig+24-6
......@@ -93,23 +93,26 @@ pub extern "NtDll" fn NtQueryDirectoryFile(
9393 FileName: ?*UNICODE_STRING,
9494 RestartScan: BOOLEAN,
9595) callconv(WINAPI) NTSTATUS;
96
9697pub extern "NtDll" fn NtCreateKeyedEvent(
9798 KeyedEventHandle: *HANDLE,
9899 DesiredAccess: ACCESS_MASK,
99100 ObjectAttributes: ?PVOID,
100101 Flags: ULONG,
101102) callconv(WINAPI) NTSTATUS;
103
102104pub extern "NtDll" fn NtReleaseKeyedEvent(
103 EventHandle: HANDLE,
104 Key: *const c_void,
105 EventHandle: ?HANDLE,
106 Key: ?*const c_void,
105107 Alertable: BOOLEAN,
106 Timeout: ?*LARGE_INTEGER,
108 Timeout: ?*const LARGE_INTEGER,
107109) callconv(WINAPI) NTSTATUS;
110
108111pub extern "NtDll" fn NtWaitForKeyedEvent(
109 EventHandle: HANDLE,
110 Key: *const c_void,
112 EventHandle: ?HANDLE,
113 Key: ?*const c_void,
111114 Alertable: BOOLEAN,
112 Timeout: ?*LARGE_INTEGER,
115 Timeout: ?*const LARGE_INTEGER,
113116) callconv(WINAPI) NTSTATUS;
114117
115118pub extern "NtDll" fn RtlSetCurrentDirectory_U(PathName: *UNICODE_STRING) callconv(WINAPI) NTSTATUS;
......@@ -121,3 +124,18 @@ pub extern "NtDll" fn NtQueryObject(
121124 ObjectInformationLength: ULONG,
122125 ReturnLength: ?*ULONG,
123126) callconv(WINAPI) NTSTATUS;
127
128pub extern "NtDll" fn RtlWakeAddressAll(
129 Address: ?*const c_void,
130) callconv(WINAPI) void;
131
132pub extern "NtDll" fn RtlWakeAddressSingle(
133 Address: ?*const c_void,
134) callconv(WINAPI) void;
135
136pub extern "NtDll" fn RtlWaitOnAddress(
137 Address: ?*const c_void,
138 CompareAddress: ?*const c_void,
139 AddressSize: SIZE_T,
140 Timeout: ?*const LARGE_INTEGER,
141) callconv(WINAPI) NTSTATUS;