ulock_sys/
lib.rs

1//! Bindings for macOS's not-entirely-public ulock API, which provides
2//! futex-like functionality.
3//!
4//! In general, caveat emptor. It's recommended that you read both the
5//! [source][sys_ulock_c] and the [header][ulock_h]. In particular, I've not
6//! documented the code very much beyond what's in the headers, aside from
7//! supported version indications.
8//!
9//! # Support
10//!
11//! This API is available on darwin 16+ (macOS 10.12+, iOS 10.0+, tvOS 10.0+,
12//! watchOS 3.0+, catalyst 13.0+), although some parts are darwin 19+ (macOS
13//! 10.15+, iOS 13.0+, TODO: find out the rest). The parts on darwin 19+ are in
14//! the `darwin19` module (it's just some additional constants, so not worth
15//! feature gating).
16//!
17//! That is to say, this is pretty well supported, over 95% of users on both
18//! macOS and iOS will have it (according to statscounter as of Jan 2021).
19//!
20//! That said, they're not public, and who knows the future, maybe Apple will
21//! remove them. But probably not — they're also used by libc++, and so if they
22//! ever went away, code that statically links libc++ would suddenly break,
23//! but... who knows, maybe apple will do it.
24//!
25//! ## "weak" linking, cargo features
26//!
27//! As a result of that (and so that you can support older versions), if you
28//! enable the `experimental-weak` feature, we'll expose a module `weak` which
29//! emulates weak linking and accesses the function via `dlsym`.
30//!
31//! Note that on aarch64 darwin (e.g. "apple silicon", the ARM64 macbooks and
32//! such) by default we don't `dlsym` even if the "experimental-weakweak"
33//! feature is on unless "weak-aarch64-macos" is also specified. This is because
34//! all OSes on these machines support the API. This can be overridden by
35//! enabling the `weak-aarch64-macos` feature.
36//!
37//! [sys_ulock_c]: https://opensource.apple.com/source/xnu/xnu-6153.11.26/bsd/kern/sys_ulock.c.auto.html
38//! [ulock_h]: https://opensource.apple.com/source/xnu/xnu-6153.11.26/bsd/sys/ulock.h.auto.html
39//!
40//! Note that the API of this crate (except for `const`s) is behind `cfg!(target_vendor = "apple")`.
41
42#![no_std]
43use cty::{c_int, c_void};
44
45#[cfg(all(feature = "experimental-weak", target_vendor = "apple"))]
46pub mod weak;
47
48#[cfg(target_vendor = "apple")]
49#[link(name = "System", kind = "dylib")]
50extern "C" {
51    pub fn __ulock_wait(op: u32, addr: *mut c_void, val: u64, micros: u32) -> c_int;
52    pub fn __ulock_wake(op: u32, addr: *mut c_void, val: u64) -> c_int;
53}
54
55/// Operation code (these are in bits 0-8)
56pub const UL_COMPARE_AND_WAIT: u32 = 1;
57/// Operation code (these are in bits 0-8)
58pub const UL_UNFAIR_LOCK: u32 = 2;
59
60/// Obsolete name for [`UL_COMPARE_AND_WAIT`]. Deprecated, but provided for porting.
61#[deprecated = "Use `UL_COMPARE_AND_WAIT`"]
62pub const UL_OSSPINLOCK: u32 = UL_COMPARE_AND_WAIT;
63/// Obsolete name for [`UL_UNFAIR_LOCK`]. Deprecated, but provided for porting.
64#[deprecated = "Use `UL_UNFAIR_LOCK`"]
65pub const UL_HANDOFFLOCK: u32 = UL_UNFAIR_LOCK;
66
67/// The portion of the API only is supported on Darwin 19 and up (macOS 10.15+,
68/// iOS 13.0+).
69pub mod darwin19 {
70    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
71    pub const UL_COMPARE_AND_WAIT_SHARED: u32 = 3;
72    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
73    pub const UL_UNFAIR_LOCK64_SHARED: u32 = 4;
74    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
75    pub const UL_COMPARE_AND_WAIT64: u32 = 5;
76    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
77    pub const UL_COMPARE_AND_WAIT64_SHARED: u32 = 6;
78    /// Flag for [`__ulock_wait`](super::__ulock_wait) (these are in bits 16-24).
79    ///
80    /// Use adaptive spinning when the thread that currently holds the unfair
81    /// lock is on core.
82    pub const ULF_WAIT_ADAPTIVE_SPIN: u32 = 0x00040000;
83}
84
85/// Flag for [`__ulock_wake`] (these are in bits 8-16)
86pub const ULF_WAKE_ALL: u32 = 0x00000100;
87/// Flag for [`__ulock_wake`] (these are in bits 8-16)
88pub const ULF_WAKE_THREAD: u32 = 0x00000200;
89
90/// Flag for [`__ulock_wait`] (these are in bits 16-24).
91///
92/// The waiter is contending on this lock for synchronization around global
93/// data. This causes the workqueue subsystem to not create new threads to
94/// offset for waiters on this lock.
95pub const ULF_WAIT_WORKQ_DATA_CONTENTION: u32 = 0x00010000;
96
97/// Flag only for [`__ulock_wait`] (these are in bits 16-24).
98///
99/// This wait is a cancelation point.
100pub const ULF_WAIT_CANCEL_POINT: u32 = 0x00020000;
101
102/// Generic flag usable with [`__ulock_wake`] or [`__ulock_wait`] (these are in bits 24-32)
103pub const ULF_NO_ERRNO: u32 = 0x01000000;
104
105// /// Mask for the `__ulock_{wait,wake}` operation code (e.g. `UL_*` constant).
106// ///
107// /// You probably don't need to use this constant.
108// pub const UL_OPCODE_MASK: u32 = 0x000000FF;
109// /// Mask for all flags. Equivalent to `!UL_OPCODE_MASK`.
110// ///
111// /// You probably don't need to use this constant.
112// pub const UL_FLAGS_MASK: u32 = 0xFFFFFF00;
113// /// In theory, a mask for the generic flags. In practice, this also contains the
114// /// flags for [`__ulock_wait`]. It's unclear if this is intentional.
115// ///
116// /// You probably don't need to use this constant.
117// pub const ULF_GENERIC_MASK: u32 = 0xFFFF0000;
118
119// Not provided because they're different on darwin19+ from before.
120//
121// pub const ULF_WAIT_MASK: u32 =
122//     ULF_NO_ERRNO | ULF_WAIT_WORKQ_DATA_CONTENTION | ULF_WAIT_CANCEL_POINT;
123// pub const ULF_WAKE_MASK: u32 = ULF_WAKE_ALL | ULF_WAKE_THREAD | ULF_NO_ERRNO;
124
125/// Alias for the function pointer of [`__ulock_wait`].
126pub type ULockWaitFn = unsafe extern "C" fn(u32, *mut c_void, u64, u32) -> c_int;
127
128/// Alias for the function pointer of [`__ulock_wake`].
129pub type ULockWakeFn = unsafe extern "C" fn(u32, *mut c_void, u64) -> c_int;