use std::ffi::{c_int, c_void};
use std::time::Duration;
use crate::sync::AtomicU32;
unsafe extern "C" {
fn __ulock_wait(operation: u32, addr: *mut c_void, value: u64, timeout_us: u32) -> c_int;
fn __ulock_wake(operation: u32, addr: *mut c_void, wake_value: u64) -> c_int;
}
const UL_COMPARE_AND_WAIT: u32 = 1;
const ULF_WAKE_ALL: u32 = 0x0000_0100;
const ULF_NO_ERRNO: u32 = 0x0100_0000;
pub(super) fn wait(word: &AtomicU32, expected: u32, timeout: Option<Duration>) {
let timeout_us = timeout.map_or(0, |d| u32::try_from(d.as_micros()).unwrap_or(u32::MAX).max(1));
let r = unsafe {
__ulock_wait(
UL_COMPARE_AND_WAIT | ULF_NO_ERRNO,
word.as_ptr().cast(),
u64::from(expected),
timeout_us,
)
};
debug_assert!(
r >= 0 || matches!(-r, libc::EINTR | libc::ETIMEDOUT | libc::EFAULT),
"unexpected __ulock_wait result: {r}"
);
}
pub(super) fn wake(word: &AtomicU32, all: bool) {
let op = UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | if all { ULF_WAKE_ALL } else { 0 };
loop {
let r = unsafe { __ulock_wake(op, word.as_ptr().cast(), 0) };
if r == -libc::EINTR {
continue;
}
debug_assert!(r >= 0 || r == -libc::ENOENT, "unexpected __ulock_wake result: {r}");
return;
}
}