Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 34 additions & 22 deletions library/std/src/sys/unix/futex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down