diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/thread/futex.rs index 5bce85735..7b26d865b 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/thread/futex.rs @@ -1,12 +1,12 @@ use crate::backend::c; bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG); /// `FUTEX_CLOCK_REALTIME` @@ -14,12 +14,10 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. -/// -/// [`futex`]: mod@crate::thread::futex +/// `FUTEX_*` operations for use with the futex syscall wrappers. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub(crate) enum Operation { /// `FUTEX_WAIT` Wait = bitcast!(c::FUTEX_WAIT), /// `FUTEX_WAKE` @@ -50,8 +48,43 @@ pub enum FutexOperation { LockPi2 = bitcast!(c::FUTEX_LOCK_PI2), } +/// `FUTEX_*` operations for use with the [`futex`] function. +/// +/// [`futex`]: fn@crate::thread::futex +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum FutexOperation { + /// `FUTEX_WAIT` + Wait = bitcast!(c::FUTEX_WAIT), + /// `FUTEX_WAKE` + Wake = bitcast!(c::FUTEX_WAKE), + /// `FUTEX_FD` + Fd = bitcast!(c::FUTEX_FD), + /// `FUTEX_REQUEUE` + Requeue = bitcast!(c::FUTEX_REQUEUE), + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = bitcast!(c::FUTEX_CMP_REQUEUE), + /// `FUTEX_WAKE_OP` + WakeOp = bitcast!(c::FUTEX_WAKE_OP), + /// `FUTEX_LOCK_PI` + LockPi = bitcast!(c::FUTEX_LOCK_PI), + /// `FUTEX_UNLOCK_PI` + UnlockPi = bitcast!(c::FUTEX_UNLOCK_PI), + /// `FUTEX_TRYLOCK_PI` + TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI), + /// `FUTEX_WAIT_BITSET` + WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET), +} + /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index fb5c71115..036b60d48 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -11,11 +11,10 @@ use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; #[cfg(linux_kernel)] use { - super::futex::FutexOperation, crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize}, crate::fd::BorrowedFd, crate::pid::Pid, - crate::thread::FutexFlags, + crate::thread::futex, crate::utils::as_mut_ptr, }; #[cfg(not(any( @@ -420,14 +419,14 @@ pub(crate) fn setresgid_thread( #[cfg(linux_kernel)] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, val2: u32, uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // the least significant four bytes of the timeout pointer are used as `val2`. + // The least-significant four bytes of the timeout pointer are used as `val2`. // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), // so we perform that exact conversion in reverse to create the pointer. let timeout = val2 as usize as *const Timespec; @@ -493,8 +492,8 @@ pub(crate) unsafe fn futex_val2( #[cfg(linux_kernel)] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -574,8 +573,8 @@ pub(crate) unsafe fn futex_timeout( ))] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 5c0fd45c2..62f6868e0 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -792,15 +792,15 @@ impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)> #[cfg(feature = "thread")] impl<'a, Num: ArgNumber> From<( - crate::backend::thread::futex::FutexOperation, - crate::thread::FutexFlags, + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, )> for ArgReg<'a, Num> { #[inline] fn from( pair: ( - crate::backend::thread::futex::FutexOperation, - crate::thread::FutexFlags, + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, ), ) -> Self { c_uint(pair.0 as u32 | pair.1.bits()) diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/thread/futex.rs index ed1a34a7e..a0a63abd4 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/thread/futex.rs @@ -1,10 +1,10 @@ bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = linux_raw_sys::general::FUTEX_PRIVATE_FLAG; /// `FUTEX_CLOCK_REALTIME` @@ -16,12 +16,10 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. -/// -/// [`futex`]: mod@crate::thread::futex +/// `FUTEX_*` operations for use with the futex syscall wrappers. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub(crate) enum Operation { /// `FUTEX_WAIT` Wait = linux_raw_sys::general::FUTEX_WAIT, /// `FUTEX_WAKE` @@ -52,8 +50,43 @@ pub enum FutexOperation { LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2, } +/// `FUTEX_*` operations for use with the [`futex`] function. +/// +/// [`futex`]: fn@crate::thread::futex +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum FutexOperation { + /// `FUTEX_WAIT` + Wait = linux_raw_sys::general::FUTEX_WAIT, + /// `FUTEX_WAKE` + Wake = linux_raw_sys::general::FUTEX_WAKE, + /// `FUTEX_FD` + Fd = linux_raw_sys::general::FUTEX_FD, + /// `FUTEX_REQUEUE` + Requeue = linux_raw_sys::general::FUTEX_REQUEUE, + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = linux_raw_sys::general::FUTEX_CMP_REQUEUE, + /// `FUTEX_WAKE_OP` + WakeOp = linux_raw_sys::general::FUTEX_WAKE_OP, + /// `FUTEX_LOCK_PI` + LockPi = linux_raw_sys::general::FUTEX_LOCK_PI, + /// `FUTEX_UNLOCK_PI` + UnlockPi = linux_raw_sys::general::FUTEX_UNLOCK_PI, + /// `FUTEX_TRYLOCK_PI` + TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI, + /// `FUTEX_WAIT_BITSET` + WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, +} + /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 907bb6457..336453131 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -5,7 +5,6 @@ //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] -use super::futex::FutexOperation; use crate::backend::c; use crate::backend::conv::{ by_mut, by_ref, c_int, c_uint, ret, ret_c_int, ret_c_int_infallible, ret_usize, slice, @@ -14,7 +13,7 @@ use crate::backend::conv::{ use crate::fd::BorrowedFd; use crate::io; use crate::pid::Pid; -use crate::thread::{ClockId, FutexFlags, NanosleepRelativeResult, Timespec}; +use crate::thread::{futex, ClockId, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; #[cfg(target_pointer_width = "32")] @@ -208,14 +207,14 @@ pub(crate) fn gettid() -> Pid { #[inline] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, val2: u32, uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // the least significant four bytes of the timeout pointer are used as `val2`. + // The least-significant four bytes of the timeout pointer are used as `val2`. // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), // so we perform that exact conversion in reverse to create the pointer. let timeout = val2 as usize as *const Timespec; @@ -247,8 +246,8 @@ pub(crate) unsafe fn futex_val2( #[inline] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -290,8 +289,8 @@ pub(crate) unsafe fn futex_timeout( #[cfg(target_pointer_width = "32")] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -321,7 +320,6 @@ unsafe fn futex_old_timespec( c_uint(val3) )) } - #[inline] pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result { unsafe { ret_c_int(syscall_readonly!(__NR_setns, fd, c_int(nstype))) } diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 1c89b96bb..0cb6687b9 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -1,81 +1,35 @@ //! Linux `futex`. //! +//! # References +//! - [Linux `futex` system call] +//! - [Linux `futex` feature] +//! //! # Safety //! //! Futex is a very low-level mechanism for implementing concurrency //! primitives. +//! +//! [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 #![allow(unsafe_code)] use core::num::NonZeroU32; use core::ptr; use core::sync::atomic::AtomicU32; +use crate::backend::thread::futex::Operation; use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; use crate::fd::{FromRawFd, OwnedFd, RawFd}; -use crate::thread::Timespec; use crate::{backend, io}; -pub use backend::thread::futex::FutexFlags; -pub use backend::thread::futex::FutexOperation; +pub use crate::timespec::Timespec; + +pub use backend::thread::futex::Flags; /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = backend::thread::futex::FUTEX_WAITERS; +pub const WAITERS: u32 = backend::thread::futex::WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = backend::thread::futex::FUTEX_OWNER_DIED; - -/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module](`self`). -/// -/// `futex(uaddr, op, val, utime, uaddr2, val3)` -/// -/// # References -/// - [Linux `futex` system call] -/// - [Linux `futex` feature] -/// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// -/// [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 -#[deprecated( - since = "0.38.35", - note = "There are now individual functions available to perform futex operations with improved type safety. See the futex module." -)] -#[inline] -pub unsafe fn futex( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - utime: *const Timespec, - uaddr2: *mut u32, - val3: u32, -) -> io::Result { - use FutexOperation::*; - - match op { - Wait | LockPi | WaitBitset | WaitRequeuePi | LockPi2 => futex_timeout( - uaddr as *const AtomicU32, - op, - flags, - val, - utime, - uaddr2 as *const AtomicU32, - val3, - ), - Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi | WakeBitset - | CmpRequeuePi => futex_val2( - uaddr as *const AtomicU32, - op, - flags, - val, - utime as usize as u32, - uaddr2 as *const AtomicU32, - val3, - ), - } -} +pub const OWNER_DIED: u32 = backend::thread::futex::OWNER_DIED; /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// @@ -93,14 +47,14 @@ pub unsafe fn futex( #[inline] pub fn wait( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::Wait, + Operation::Wait, flags, val, timeout @@ -133,18 +87,8 @@ pub fn wait( /// [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] -pub fn wake(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Wake, - flags, - val, - 0, - ptr::null(), - 0, - ) - } +pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` @@ -161,18 +105,9 @@ pub fn wake(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result /// [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] -pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { +pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Fd, - flags, - val, - 0, - ptr::null(), - 0, - ) - .map(|val| { + 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) @@ -196,22 +131,12 @@ pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result #[inline] pub fn requeue( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Requeue, - flags, - val, - val2, - uaddr2, - 0, - ) - } + unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` @@ -230,23 +155,13 @@ pub fn requeue( #[inline] pub fn cmp_requeue( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, val3: u32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::CmpRequeue, - flags, - val, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) } } /// `FUTEX_OP_*` operations for use with [`wake_op`]. @@ -309,7 +224,7 @@ pub enum WakeOpCmp { #[inline] pub fn wake_op( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, @@ -325,17 +240,7 @@ pub fn wake_op( let val3 = ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::WakeOp, - flags, - val, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` @@ -352,11 +257,11 @@ pub fn wake_op( /// [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] -pub fn lock_pi(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { +pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::LockPi, + Operation::LockPi, flags, 0, timeout @@ -389,18 +294,9 @@ pub fn lock_pi(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) /// [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] -pub fn unlock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result<()> { +pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::UnlockPi, - flags, - 0, - 0, - ptr::null(), - 0, - ) - .map(|val| { + 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" @@ -423,22 +319,13 @@ pub fn unlock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result<()> { /// [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] -pub fn trylock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result { +pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::TrylockPi, - flags, - 0, - 0, - ptr::null(), - 0, - ) - .map(|ret| ret == 0) + futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0) } } -/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout/val2, NULL, val3)` +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` /// /// # References /// - [Linux `futex` system call] @@ -454,15 +341,15 @@ pub fn trylock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result { #[inline] pub fn wait_bitset( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, val3: NonZeroU32, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::WaitBitset, + Operation::WaitBitset, flags, val, timeout @@ -497,14 +384,14 @@ pub fn wait_bitset( #[inline] pub fn wake_bitset( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val3: NonZeroU32, ) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( + futex_val2( uaddr, - FutexOperation::WakeBitset, + Operation::WakeBitset, flags, val, 0, @@ -530,15 +417,15 @@ pub fn wake_bitset( #[inline] pub fn wait_requeue_pi( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, uaddr2: &AtomicU32, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::WaitRequeuePi, + Operation::WaitRequeuePi, flags, val, timeout @@ -573,22 +460,12 @@ pub fn wait_requeue_pi( #[inline] pub fn cmp_requeue_pi( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val2: u32, uaddr2: &AtomicU32, val3: u32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::CmpRequeuePi, - flags, - 1, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` @@ -605,11 +482,11 @@ pub fn cmp_requeue_pi( /// [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] -pub fn lock_pi2(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { +pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::LockPi2, + Operation::LockPi2, flags, 0, timeout diff --git a/src/thread/mod.rs b/src/thread/mod.rs index cb5d566f3..ce62d8f68 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -13,12 +13,16 @@ mod prctl; #[cfg(linux_kernel)] mod setns; +#[allow(deprecated)] +#[cfg(linux_kernel)] +pub use crate::backend::thread::futex::FutexOperation; +#[cfg(linux_kernel)] +pub use crate::thread::futex::{ + Flags as FutexFlags, OWNER_DIED as FUTEX_OWNER_DIED, WAITERS as FUTEX_WAITERS, +}; #[cfg(not(target_os = "redox"))] pub use clock::*; #[cfg(linux_kernel)] -#[allow(deprecated)] -pub use futex::{futex, FutexFlags, FutexOperation, FUTEX_OWNER_DIED, FUTEX_WAITERS}; -#[cfg(linux_kernel)] pub use id::{ gettid, set_thread_gid, set_thread_groups, set_thread_res_gid, set_thread_res_uid, set_thread_uid, Gid, Pid, RawGid, RawPid, RawUid, Uid, @@ -29,3 +33,59 @@ pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets pub use prctl::*; #[cfg(linux_kernel)] pub use setns::*; + +/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module]. +/// +/// `futex(uaddr, op, val, utime, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [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 +/// [futex module]: mod@crate::thread::futex +#[cfg(linux_kernel)] +#[allow(unsafe_code, deprecated)] +#[inline] +pub unsafe fn futex( + uaddr: *mut u32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + utime: *const Timespec, + uaddr2: *mut u32, + val3: u32, +) -> crate::io::Result { + use crate::backend::thread::futex::Operation; + use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; + use core::mem::transmute; + use core::sync::atomic::AtomicU32; + use FutexOperation::*; + + match op { + Wait | LockPi | WaitBitset => futex_timeout( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime, + uaddr2 as *const AtomicU32, + val3, + ), + Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi => futex_val2( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime as usize as u32, + uaddr2 as *const AtomicU32, + val3, + ), + } +} diff --git a/tests/thread/futex.rs b/tests/thread/futex.rs index 918b7dac1..cd1df57e5 100644 --- a/tests/thread/futex.rs +++ b/tests/thread/futex.rs @@ -1,22 +1,20 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use rustix::{ - io::Errno, - thread::{futex, FutexFlags}, -}; +use rustix::io::Errno; +use rustix::thread::futex; #[test] fn test_lock_unlock_pi() { let lock = AtomicU32::new(0); - futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap(); + futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap(); assert_ne!(lock.load(Ordering::SeqCst), 0); - let err = unsafe { futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap_err() }; + let err = futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap_err(); assert_eq!(err, Errno::DEADLK); - futex::unlock_pi(&lock, FutexFlags::empty()).unwrap(); + futex::unlock_pi(&lock, futex::Flags::empty()).unwrap(); assert_eq!(lock.load(Ordering::SeqCst), 0); - let err = futex::unlock_pi(&lock, FutexFlags::empty()).unwrap_err(); + let err = futex::unlock_pi(&lock, futex::Flags::empty()).unwrap_err(); assert_eq!(err, Errno::PERM); } @@ -25,7 +23,7 @@ fn test_lock_unlock_pi() { fn test_wait_wake() { let lock = std::sync::Arc::new(AtomicU32::new(0)); - match futex::wait(&lock, FutexFlags::empty(), 1, None) { + match futex::wait(&lock, futex::Flags::empty(), 1, None) { Ok(()) => panic!("Nobody should be waking us!"), Err(Errno::AGAIN) => { assert_eq!(lock.load(Ordering::SeqCst), 0, "the lock should still be 0") @@ -38,10 +36,10 @@ fn test_wait_wake() { move || { std::thread::sleep(std::time::Duration::from_millis(1)); lock.store(1, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); std::thread::sleep(std::time::Duration::from_millis(50)); - match futex::wait(&lock, FutexFlags::empty(), 1, None) { + match futex::wait(&lock, futex::Flags::empty(), 1, None) { Ok(()) => (), Err(Errno::AGAIN) => { assert_eq!(lock.load(Ordering::SeqCst), 2, "the lock should now be 2") @@ -51,14 +49,14 @@ fn test_wait_wake() { } }); - match futex::wait(&lock, FutexFlags::empty(), 0, None) { + match futex::wait(&lock, futex::Flags::empty(), 0, None) { Ok(()) => (), Err(Errno::AGAIN) => assert_eq!(lock.load(Ordering::SeqCst), 1, "the lock should now be 1"), Err(err) => panic!("{err}"), } lock.store(2, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); other.join().unwrap(); } @@ -66,13 +64,13 @@ fn test_wait_wake() { #[cfg(feature = "std")] #[test] fn test_timeout() { - use rustix::fs::Timespec; + use rustix::thread::futex::Timespec; let lock = AtomicU32::new(0); let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: 1, @@ -84,7 +82,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: 0, @@ -96,7 +94,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: -1,