//! Linux `futex`. //! //! Futex is a very low-level mechanism for implementing concurrency primitives //! such as mutexes, rwlocks, and condvars. For a higher-level API that //! provides those abstractions, see [rustix-futex-sync]. //! //! # Examples //! //! ``` //! use rustix::thread::futex; //! use std::sync::atomic::AtomicU32; //! //! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> { //! // Wake up one waiter. //! futex::wake(futex, futex::Flags::PRIVATE, 1)?; //! # Ok(()) //! # } //! ``` //! //! # References //! - [Linux `futex` system call] //! - [Linux `futex` feature] //! //! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html //! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html //! [rustix-futex-sync]: https://crates.io/crates/rustix-futex-sync #![allow(unsafe_code)]
use core::ffi::c_void; use core::num::NonZeroU32; use core::ptr; use core::sync::atomic::AtomicU32;
usecrate::backend::thread::futex::Operation; usecrate::backend::thread::syscalls::{futex_timeout, futex_val2}; usecrate::fd::{FromRawFd as _, OwnedFd, RawFd}; usecrate::{backend, io};
/// `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn wait(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
timeout: Option<&Timespec>,
) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_timeout(uaddr, Operation::Wait, flags, val, timeout, ptr::null(), 0).map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<usize> { // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) }
}
/// `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<OwnedFd> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| { let fd = val as RawFd;
debug_assert_eq!(fd as usize, val, "return value should be a valid fd");
OwnedFd::from_raw_fd(fd)
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn requeue(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
val2: u32,
uaddr2: &AtomicU32,
) -> io::Result<usize> { // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) }
}
/// `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn cmp_requeue(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
val2: u32,
uaddr2: &AtomicU32,
val3: u32,
) -> io::Result<usize> { // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) }
}
/// `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] #[allow(clippy::too_many_arguments)] pubfn wake_op(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
val2: u32,
uaddr2: &AtomicU32,
op: WakeOp,
cmp: WakeOpCmp,
oparg: u16,
cmparg: u16,
) -> io::Result<usize> { if oparg >= 1 << 12 || cmparg >= 1 << 12 { return Err(io::Errno::INVAL);
}
let val3 =
((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32);
// SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) }
}
/// `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option<&Timespec>) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_timeout(uaddr, Operation::LockPi, flags, 0, timeout, ptr::null(), 0).map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<bool> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0)
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn wait_bitset(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
timeout: Option<&Timespec>,
val3: NonZeroU32,
) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_timeout(
uaddr,
Operation::WaitBitset,
flags,
val,
timeout,
ptr::null(),
val3.get(),
)
.map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn wake_bitset(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
val3: NonZeroU32,
) -> io::Result<usize> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_val2(
uaddr,
Operation::WakeBitset,
flags,
val, 0,
ptr::null(),
val3.get(),
)
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn wait_requeue_pi(
uaddr: &AtomicU32,
flags: Flags,
val: u32,
timeout: Option<&Timespec>,
uaddr2: &AtomicU32,
) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_timeout(
uaddr,
Operation::WaitRequeuePi,
flags,
val,
timeout,
uaddr2, 0,
)
.map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn cmp_requeue_pi(
uaddr: &AtomicU32,
flags: Flags,
val2: u32,
uaddr2: &AtomicU32,
val3: u32,
) -> io::Result<usize> { // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) }
}
/// `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization /// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pubfn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option<&Timespec>) -> io::Result<()> { // SAFETY: The raw pointers come from references or null. unsafe {
futex_timeout(uaddr, Operation::LockPi2, flags, 0, timeout, ptr::null(), 0).map(|val| {
debug_assert_eq!(
val, 0, "The return value should always equal zero, if the call is successful"
);
})
}
}
/// For use with [`waitv`]. #[repr(C)] #[derive(Debug, Copy, Clone)] #[non_exhaustive] pubstruct Wait { /// The expected value. pub val: u64, /// The address to wait for. pub uaddr: WaitPtr, /// The type and size of futex to perform. pub flags: WaitFlags,
/// Reserved for future use. pub(crate) __reserved: u32,
}
/// `futex_waitv(waiters.as_ptr(), waiters.len(), flags, timeout, clockd)`— /// Wait on an array of futexes, wake on any. /// /// This requires Linux ≥ 5.16. /// /// # References /// - [Linux] /// /// [Linux]: https://www.kernel.org/doc/html/latest/userspace-api/futex2.html #[inline] pubfn waitv(
waiters: &[Wait],
flags: WaitvFlags,
timeout: Option<&Timespec>,
clockid: ClockId,
) -> io::Result<usize> {
backend::thread::syscalls::futex_waitv(waiters, flags, timeout, clockid)
}
bitflags::bitflags! { /// Flags for use with the flags argument in [`waitv`]. /// /// At this time, no flags are defined. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pubstruct WaitvFlags: u32 { /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> const _ = !0;
}
}
#[cfg(linux_raw)] #[cfg(test)] mod tests { usesuper::*;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.