Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT on Linux.
This commit is contained in:
parent
da4ef044c1
commit
87299298d9
2 changed files with 38 additions and 22 deletions
|
@ -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")]
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue