1use super::*;
3
4#[derive(Clone, Copy, PartialEq)]
11#[repr(C)]
12pub struct ULockApi {
13 pub ulock_wait: ULockWaitFn,
15 pub ulock_wake: ULockWakeFn,
17}
18
19#[cfg(all(
20 target_os = "macos",
21 target_arch = "aarch64",
22 not(feature = "weak-aarch64-macos")
23))]
24mod imp {
25 use super::*;
26 pub(super) static API: ULockApi = ULockApi {
27 ulock_wait: crate::__ulock_wait,
28 ulock_wake: crate::__ulock_wake,
29 };
30
31 #[inline]
32 pub(super) unsafe fn ulock_wait(
33 op: u32,
34 addr: *mut c_void,
35 val: u64,
36 micros: u32,
37 ) -> Result<c_int, ApiUnsupported> {
38 Ok(crate::__ulock_wait(op, addr, val, micros))
39 }
40
41 #[inline]
42 pub(super) unsafe fn ulock_wake(
43 op: u32,
44 addr: *mut c_void,
45 val: u64,
46 ) -> Result<c_int, ApiUnsupported> {
47 Ok(crate::__ulock_wake(op, addr, val))
48 }
49
50 #[inline]
51 pub(super) fn get() -> Result<&'static ULockApi, ApiUnsupported> {
52 Ok(&API)
53 }
54}
55
56#[cfg(not(all(
57 target_os = "macos",
58 target_arch = "aarch64",
59 not(feature = "weak-aarch64-macos")
60)))]
61mod imp {
62 use super::*;
63 use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering::*};
64
65 #[repr(C)]
66 struct MaybeULockApi {
67 ulock_wait: AtomicUsize,
68 ulock_wake: AtomicUsize,
69 }
70
71 const NOT_INIT: u8 = 0;
72 const BAD_INIT: u8 = 1;
73 const GOOD_INIT: u8 = 2;
74 static INIT_STATE: AtomicU8 = AtomicU8::new(NOT_INIT);
75
76 static API: MaybeULockApi = MaybeULockApi {
77 ulock_wait: AtomicUsize::new(0),
78 ulock_wake: AtomicUsize::new(0),
79 };
80
81 #[inline]
82 pub(super) unsafe fn ulock_wait(
83 op: u32,
84 addr: *mut c_void,
85 val: u64,
86 micros: u32,
87 ) -> Result<cty::c_int, ApiUnsupported> {
88 let func: Option<ULockWaitFn> = core::mem::transmute(API.ulock_wait.load(Relaxed));
89 if let Some(func) = func {
90 return Ok((func)(op, addr, val, micros));
91 }
92 ulock_wait_outline(op, addr, val, micros)
93 }
94
95 #[cold]
96 unsafe fn ulock_wait_outline(
97 op: u32,
98 addr: *mut c_void,
99 val: u64,
100 micros: u32,
101 ) -> Result<cty::c_int, ApiUnsupported> {
102 maybe_init().and_then(|_| {
103 let func: ULockWaitFn = core::mem::transmute(API.ulock_wait.load(Relaxed));
104 Ok(func(op, addr, val, micros))
105 })
106 }
107
108 #[inline]
109 pub(super) unsafe fn ulock_wake(
110 op: u32,
111 addr: *mut c_void,
112 val: u64,
113 ) -> Result<cty::c_int, ApiUnsupported> {
114 let func: Option<ULockWakeFn> = core::mem::transmute(API.ulock_wake.load(Relaxed));
115 if let Some(func) = func {
116 return Ok((func)(op, addr, val));
117 }
118 ulock_wake_outline(op, addr, val)
119 }
120
121 #[cold]
122 unsafe fn ulock_wake_outline(
123 op: u32,
124 addr: *mut c_void,
125 val: u64,
126 ) -> Result<cty::c_int, ApiUnsupported> {
127 maybe_init().and_then(|_| {
128 let func: ULockWakeFn = core::mem::transmute(API.ulock_wake.load(Relaxed));
129 Ok(func(op, addr, val))
130 })
131 }
132
133 #[inline]
134 pub(super) fn get() -> Result<&'static super::ULockApi, ApiUnsupported> {
135 maybe_init()?;
136 Ok(unsafe { &*(&API as *const _ as *const super::ULockApi) })
138 }
139
140 #[inline]
141 fn maybe_init() -> Result<(), ApiUnsupported> {
142 match INIT_STATE.load(Acquire) {
143 BAD_INIT => return Err(ApiUnsupported(())),
144 GOOD_INIT => {}
145 NOT_INIT => init()?,
146 v => {
147 debug_assert!(false, "unknown state (please report this bug): {}", v);
148 unsafe { core::hint::unreachable_unchecked() };
149 }
150 }
151 debug_assert_eq!(INIT_STATE.load(Acquire), GOOD_INIT);
152 debug_assert!(API.ulock_wait.load(Relaxed) != 0);
153 debug_assert!(API.ulock_wake.load(Relaxed) != 0);
154 Ok(())
155 }
156
157 fn init() -> Result<(), ApiUnsupported> {
158 let ulock_wait = unsafe { load_sym("__ulock_wait\0") as usize };
159 let ulock_wake = unsafe { load_sym("__ulock_wake\0") as usize };
160 if ulock_wait == 0 || ulock_wake == 0 {
161 let old = match INIT_STATE.compare_exchange(NOT_INIT, BAD_INIT, AcqRel, Acquire) {
162 Ok(v) => v,
163 Err(v) => v,
164 };
165 return match old {
166 NOT_INIT => Err(ApiUnsupported(())),
167 GOOD_INIT => Ok(()),
168 BAD_INIT => Err(ApiUnsupported(())),
169 v => {
170 debug_assert!(false, "unknown state (please report this bug): {}", v);
171 unsafe { core::hint::unreachable_unchecked() };
172 }
173 };
174 }
175
176 let old = API
177 .ulock_wait
178 .compare_exchange(0, ulock_wait, Relaxed, Relaxed);
179 debug_assert!(old.is_ok() || old == Err(ulock_wait));
180
181 let old = API
182 .ulock_wake
183 .compare_exchange(0, ulock_wake, Relaxed, Relaxed);
184 debug_assert!(old.is_ok() || old == Err(ulock_wake));
185
186 if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
187 let old = INIT_STATE.swap(GOOD_INIT, SeqCst);
191 debug_assert_ne!(old, BAD_INIT);
192 } else {
193 INIT_STATE.store(GOOD_INIT, Release);
194 }
195 Ok(())
196 }
197
198 #[inline]
199 unsafe fn load_sym(s: &str) -> *mut c_void {
200 const RTLD_DEFAULT: *mut c_void = -2isize as *mut c_void;
201 extern "C" {
202 fn dlsym(h: *mut c_void, s: *const cty::c_char) -> *mut c_void;
203 }
204 debug_assert_eq!(s.as_bytes()[s.len() - 1], b'\0');
205 dlsym(RTLD_DEFAULT, s.as_ptr().cast())
206 }
207
208 #[used]
211 #[cfg(link_section = "__DATA,__mod_init_func")]
212 static CTOR: extern "C" fn() = init_function;
213 #[allow(dead_code)]
214 extern "C" fn init_function() {
215 drop(init());
216 }
217}
218
219#[inline]
229pub unsafe fn ulock_wait(
230 op: u32,
231 addr: *mut c_void,
232 val: u64,
233 micros: u32,
234) -> Result<cty::c_int, ApiUnsupported> {
235 imp::ulock_wait(op, addr, val, micros)
236}
237
238#[inline]
248pub unsafe fn ulock_wake(
249 op: u32,
250 addr: *mut c_void,
251 val: u64,
252) -> Result<cty::c_int, ApiUnsupported> {
253 imp::ulock_wake(op, addr, val)
254}
255
256impl ULockApi {
257 #[inline]
263 pub fn get() -> Result<&'static Self, ApiUnsupported> {
264 imp::get()
265 }
266}
267
268#[derive(Clone, PartialEq, Copy)]
270pub struct ApiUnsupported(());
271
272impl core::fmt::Display for ApiUnsupported {
273 #[cold]
274 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275 f.write_str("ulock api unsupported")
276 }
277}
278
279impl core::fmt::Debug for ApiUnsupported {
280 #[cold]
281 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
282 f.write_str("ulock api unsupported")
283 }
284}