all(target_os = "emscripten", target_feature = "atomics")
))]
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use crate::convert::TryInto;
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use crate::ptr::null;
use crate::sync::atomic::AtomicI32;
use crate::time::Duration;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) -> bool {
- let timespec = timeout.and_then(|d| {
- Some(libc::timespec {
- // Sleep forever if the timeout is longer than fits in a timespec.
- tv_sec: d.as_secs().try_into().ok()?,
- // This conversion never truncates, as subsec_nanos is always <1e9.
- tv_nsec: d.subsec_nanos() as _,
- })
- });
- let r = unsafe {
- libc::syscall(
- libc::SYS_futex,
- futex as *const AtomicI32,
- libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
- expected,
- timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
- )
- };
- !(r < 0 && super::os::errno() == libc::ETIMEDOUT)
+ use super::time::Instant;
+ use crate::ptr::null;
+ use crate::sync::atomic::Ordering::Relaxed;
+
+ // Calculate the timeout as an absolute timespec.
+ let timespec =
+ timeout.and_then(|d| Some(Instant::now().checked_add_duration(&d)?.as_timespec()));
+
+ loop {
+ // No need to wait if the value already changed.
+ if futex.load(Relaxed) != expected {
+ return true;
+ }
+
+ // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
+ // absolute time rather than a relative time.
+ let r = unsafe {
+ libc::syscall(
+ libc::SYS_futex,
+ futex as *const AtomicI32,
+ libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+ expected,
+ timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
+ null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+ !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+ )
+ };
+
+ match (r < 0).then(super::os::errno) {
+ Some(libc::ETIMEDOUT) => return false,
+ Some(libc::EINTR) => continue,
+ _ => return true,
+ }
+ }
}
#[cfg(target_os = "emscripten")]