Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Linux Mutex and Condvar with futex based ones. #95035

Merged
merged 14 commits into from
Apr 5, 2022
Merged
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 {
m-ou-se marked this conversation as resolved.
Show resolved Hide resolved
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.
m-ou-se marked this conversation as resolved.
Show resolved Hide resolved
let timespec =
timeout.and_then(|d| Some(Instant::now().checked_add_duration(&d)?.as_timespec()));
bjorn3 marked this conversation as resolved.
Show resolved Hide resolved

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