Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.

This commit is contained in:
Mara Bos 2022-03-24 09:51:48 +01:00
parent da4ef044c1
commit 87299298d9
2 changed files with 38 additions and 22 deletions

View file

@ -4,33 +4,45 @@
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")]

View file

@ -299,6 +299,10 @@ mod inner {
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
pub(in crate::sys::unix) fn as_timespec(&self) -> libc::timespec {
self.t.t
}
}
impl fmt::Debug for Instant {