1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Bindings for macOS's not-entirely-public ulock API, which provides
//! futex-like functionality.
//!
//! In general, caveat emptor. It's recommended that you read both the
//! [source][sys_ulock_c] and the [header][ulock_h]. In particular, I've not
//! documented the code very much beyond what's in the headers, aside from
//! supported version indications.
//!
//! # Support
//!
//! This API is available on darwin 16+ (macOS 10.12+, iOS 10.0+, tvOS 10.0+,
//! watchOS 3.0+, catalyst 13.0+), although some parts are darwin 19+ (macOS
//! 10.15+, iOS 13.0+, TODO: find out the rest). The parts on darwin 19+ are in
//! the `darwin19` module (it's just some additional constants, so not worth
//! feature gating).
//!
//! That is to say, this is pretty well supported, over 95% of users on both
//! macOS and iOS will have it (according to statscounter as of Jan 2021).
//!
//! That said, they're not public, and who knows the future, maybe Apple will
//! remove them. But probably not — they're also used by libc++, and so if they
//! ever went away, code that statically links libc++ would suddenly break,
//! but... who knows, maybe apple will do it.
//!
//! ## "weak" linking, cargo features
//!
//! As a result of that (and so that you can support older versions), if you
//! enable the `experimental-weak` feature, we'll expose a module `weak` which
//! emulates weak linking and accesses the function via `dlsym`.
//!
//! Note that on aarch64 darwin (e.g. "apple silicon", the ARM64 macbooks and
//! such) by default we don't `dlsym` even if the "experimental-weakweak"
//! feature is on unless "weak-aarch64-macos" is also specified. This is because
//! all OSes on these machines support the API. This can be overridden by
//! enabling the `weak-aarch64-macos` feature.
//!
//! [sys_ulock_c]: https://opensource.apple.com/source/xnu/xnu-6153.11.26/bsd/kern/sys_ulock.c.auto.html
//! [ulock_h]: https://opensource.apple.com/source/xnu/xnu-6153.11.26/bsd/sys/ulock.h.auto.html
//!
//! Note that the API of this crate (except for `const`s) is behind `cfg!(target_vendor = "apple")`.

#![no_std]
use cty::{c_int, c_void};

#[cfg(all(feature = "experimental-weak", target_vendor = "apple"))]
pub mod weak;

#[cfg(target_vendor = "apple")]
#[link(name = "System", kind = "dylib")]
extern "C" {
    pub fn __ulock_wait(op: u32, addr: *mut c_void, val: u64, micros: u32) -> c_int;
    pub fn __ulock_wake(op: u32, addr: *mut c_void, val: u64) -> c_int;
}

/// Operation code (these are in bits 0-8)
pub const UL_COMPARE_AND_WAIT: u32 = 1;
/// Operation code (these are in bits 0-8)
pub const UL_UNFAIR_LOCK: u32 = 2;

/// Obsolete name for [`UL_COMPARE_AND_WAIT`]. Deprecated, but provided for porting.
#[deprecated = "Use `UL_COMPARE_AND_WAIT`"]
pub const UL_OSSPINLOCK: u32 = UL_COMPARE_AND_WAIT;
/// Obsolete name for [`UL_UNFAIR_LOCK`]. Deprecated, but provided for porting.
#[deprecated = "Use `UL_UNFAIR_LOCK`"]
pub const UL_HANDOFFLOCK: u32 = UL_UNFAIR_LOCK;

/// The portion of the API only is supported on Darwin 19 and up (macOS 10.15+,
/// iOS 13.0+).
pub mod darwin19 {
    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
    pub const UL_COMPARE_AND_WAIT_SHARED: u32 = 3;
    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
    pub const UL_UNFAIR_LOCK64_SHARED: u32 = 4;
    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
    pub const UL_COMPARE_AND_WAIT64: u32 = 5;
    /// Operation code (these are in bits 0-8). Requires Darwin 19+.
    pub const UL_COMPARE_AND_WAIT64_SHARED: u32 = 6;
    /// Flag for [`__ulock_wait`](super::__ulock_wait) (these are in bits 16-24).
    ///
    /// Use adaptive spinning when the thread that currently holds the unfair
    /// lock is on core.
    pub const ULF_WAIT_ADAPTIVE_SPIN: u32 = 0x00040000;
}

/// Flag for [`__ulock_wake`] (these are in bits 8-16)
pub const ULF_WAKE_ALL: u32 = 0x00000100;
/// Flag for [`__ulock_wake`] (these are in bits 8-16)
pub const ULF_WAKE_THREAD: u32 = 0x00000200;

/// Flag for [`__ulock_wait`] (these are in bits 16-24).
///
/// The waiter is contending on this lock for synchronization around global
/// data. This causes the workqueue subsystem to not create new threads to
/// offset for waiters on this lock.
pub const ULF_WAIT_WORKQ_DATA_CONTENTION: u32 = 0x00010000;

/// Flag only for [`__ulock_wait`] (these are in bits 16-24).
///
/// This wait is a cancelation point.
pub const ULF_WAIT_CANCEL_POINT: u32 = 0x00020000;

/// Generic flag usable with [`__ulock_wake`] or [`__ulock_wait`] (these are in bits 24-32)
pub const ULF_NO_ERRNO: u32 = 0x01000000;

// /// Mask for the `__ulock_{wait,wake}` operation code (e.g. `UL_*` constant).
// ///
// /// You probably don't need to use this constant.
// pub const UL_OPCODE_MASK: u32 = 0x000000FF;
// /// Mask for all flags. Equivalent to `!UL_OPCODE_MASK`.
// ///
// /// You probably don't need to use this constant.
// pub const UL_FLAGS_MASK: u32 = 0xFFFFFF00;
// /// In theory, a mask for the generic flags. In practice, this also contains the
// /// flags for [`__ulock_wait`]. It's unclear if this is intentional.
// ///
// /// You probably don't need to use this constant.
// pub const ULF_GENERIC_MASK: u32 = 0xFFFF0000;

// Not provided because they're different on darwin19+ from before.
//
// pub const ULF_WAIT_MASK: u32 =
//     ULF_NO_ERRNO | ULF_WAIT_WORKQ_DATA_CONTENTION | ULF_WAIT_CANCEL_POINT;
// pub const ULF_WAKE_MASK: u32 = ULF_WAKE_ALL | ULF_WAKE_THREAD | ULF_NO_ERRNO;

/// Alias for the function pointer of [`__ulock_wait`].
pub type ULockWaitFn = unsafe extern "C" fn(u32, *mut c_void, u64, u32) -> c_int;

/// Alias for the function pointer of [`__ulock_wake`].
pub type ULockWakeFn = unsafe extern "C" fn(u32, *mut c_void, u64) -> c_int;