diff --git a/Cargo.toml b/Cargo.toml index 06de21b7f..9a12c0987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,6 +134,9 @@ event = [] # Enable `rustix::fs::*`. fs = [] +# Enable `rustix::futex::*`. +futex = [] + # Enable `rustix::io_uring::*` (on platforms that support it). io_uring = ["event", "fs", "net", "linux-raw-sys/io_uring"] @@ -144,7 +147,7 @@ mount = [] net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether", "linux-raw-sys/xdp"] # Enable `rustix::thread::*`. -thread = ["linux-raw-sys/prctl"] +thread = ["linux-raw-sys/prctl", "futex"] # Enable `rustix::process::*`. process = ["linux-raw-sys/prctl"] @@ -190,6 +193,7 @@ runtime = ["linux-raw-sys/prctl"] all-apis = [ "event", "fs", + "futex", "io_uring", "mm", "mount", diff --git a/README.md b/README.md index 9f437b1a4..20e43e217 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ by default. The rest of the API is conditional with cargo feature flags: | ---------- | -------------------------------------------------------------- | | `event` | [`rustix::event`]—Polling and event operations. | | `fs` | [`rustix::fs`]—Filesystem operations. | +| `futex` | [`rustix::futex`]—Linux futex operations. | | `io_uring` | [`rustix::io_uring`]—Linux io_uring. | | `mm` | [`rustix::mm`]—Memory map operations. | | `mount` | [`rustix::mount`]—Linux mount API. | @@ -80,6 +81,7 @@ by default. The rest of the API is conditional with cargo feature flags: [`rustix::event`]: https://docs.rs/rustix/*/rustix/event/index.html [`rustix::fs`]: https://docs.rs/rustix/*/rustix/fs/index.html +[`rustix::futex`]: https://docs.rs/rustix/*/rustix/futex/index.html [`rustix::io_uring`]: https://docs.rs/rustix/*/rustix/io_uring/index.html [`rustix::mm`]: https://docs.rs/rustix/*/rustix/mm/index.html [`rustix::mount`]: https://docs.rs/rustix/*/rustix/mount/index.html diff --git a/src/backend/libc/futex/mod.rs b/src/backend/libc/futex/mod.rs new file mode 100644 index 000000000..1e0181a99 --- /dev/null +++ b/src/backend/libc/futex/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/src/backend/libc/futex/syscalls.rs b/src/backend/libc/futex/syscalls.rs new file mode 100644 index 000000000..2964a4a06 --- /dev/null +++ b/src/backend/libc/futex/syscalls.rs @@ -0,0 +1,202 @@ +use crate::backend::c; +use crate::backend::conv::ret_usize; +use crate::futex; +use crate::io; +use crate::timespec::Timespec; +use core::sync::atomic::AtomicU32; + +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, + op: super::types::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 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; + + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) + ))] + { + // TODO: Upstream this to the libc crate. + #[allow(non_upper_case_globals)] + const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; + + syscall! { + fn futex_time64( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex_time64 -> c::ssize_t + } + + ret_usize(futex_time64( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout, + uaddr2, + val3, + )) + } + + #[cfg(any( + target_pointer_width = "64", + target_arch = "aarch64", + target_arch = "x86_64" + ))] + { + syscall! { + fn futex( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout.cast(), + uaddr2, + val3, + ) as isize) + } +} + +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: super::types::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) + ))] + { + // TODO: Upstream this to the libc crate. + #[allow(non_upper_case_globals)] + const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; + + syscall! { + fn futex_time64( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex_time64 -> c::ssize_t + } + + ret_usize(futex_time64( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout, + uaddr2, + val3, + )) + .or_else(|err| { + // See the comments in `rustix_clock_gettime_via_syscall` about + // emulation. + if err == io::Errno::NOSYS { + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) + } else { + Err(err) + } + }) + } + + #[cfg(any( + target_pointer_width = "64", + target_arch = "aarch64", + target_arch = "x86_64" + ))] + { + syscall! { + fn futex( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout.cast(), + uaddr2, + val3, + ) as isize) + } +} + +#[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) +))] +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, + op: super::types::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + syscall! { + fn futex( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_old_timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + let old_timeout = if timeout.is_null() { + None + } else { + Some(linux_raw_sys::general::__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) + }; + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + old_timeout + .as_ref() + .map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec) + .unwrap_or(core::ptr::null()), + uaddr2, + val3, + ) as isize) +} diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/futex/types.rs similarity index 78% rename from src/backend/libc/thread/futex.rs rename to src/backend/libc/futex/types.rs index 5bce85735..36915e431 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/futex/types.rs @@ -1,12 +1,12 @@ use crate::backend::c; bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with functions in [`futex`]. /// - /// [`futex`]: mod@crate::thread::futex + /// [`futex`]: crate::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,12 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. +/// `FUTEX_*` operations for use with functions in [`futex`]. /// -/// [`futex`]: mod@crate::thread::futex +/// [`futex`]: crate::futex #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub enum Operation { /// `FUTEX_WAIT` Wait = bitcast!(c::FUTEX_WAIT), /// `FUTEX_WAKE` @@ -51,7 +51,7 @@ pub enum FutexOperation { } /// `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/mod.rs b/src/backend/libc/mod.rs index 9ecb09f12..92853ba5d 100644 --- a/src/backend/libc/mod.rs +++ b/src/backend/libc/mod.rs @@ -107,6 +107,9 @@ pub(crate) mod event; #[cfg(not(windows))] #[cfg(feature = "fs")] pub(crate) mod fs; +#[cfg(linux_kernel)] +#[cfg(feature = "futex")] +pub(crate) mod futex; pub(crate) mod io; #[cfg(linux_kernel)] #[cfg(feature = "io_uring")] diff --git a/src/backend/libc/thread/mod.rs b/src/backend/libc/thread/mod.rs index 4f8c87cd2..40e0d1135 100644 --- a/src/backend/libc/thread/mod.rs +++ b/src/backend/libc/thread/mod.rs @@ -1,4 +1,2 @@ -#[cfg(linux_kernel)] -pub(crate) mod futex; #[cfg(not(windows))] pub(crate) mod syscalls; diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index fb5c71115..3f369cedd 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -8,14 +8,11 @@ use crate::thread::{NanosleepRelativeResult, Timespec}; #[cfg(all(target_env = "gnu", fix_y2038))] use crate::timespec::LibcTimespec; 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::backend::conv::{borrowed_fd, ret_c_int}, crate::fd::BorrowedFd, crate::pid::Pid, - crate::thread::FutexFlags, crate::utils::as_mut_ptr, }; #[cfg(not(any( @@ -417,205 +414,6 @@ pub(crate) fn setresgid_thread( unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } } -#[cfg(linux_kernel)] -pub(crate) unsafe fn futex_val2( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - 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 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; - - #[cfg(all( - target_pointer_width = "32", - not(any(target_arch = "aarch64", target_arch = "x86_64")) - ))] - { - // TODO: Upstream this to the libc crate. - #[allow(non_upper_case_globals)] - const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; - - syscall! { - fn futex_time64( - uaddr: *const AtomicU32, - futex_op: c::c_int, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32 - ) via SYS_futex_time64 -> c::ssize_t - } - - ret_usize(futex_time64( - uaddr, - op as i32 | flags.bits() as i32, - val, - timeout, - uaddr2, - val3, - )) - } - - #[cfg(any( - target_pointer_width = "64", - target_arch = "aarch64", - target_arch = "x86_64" - ))] - { - syscall! { - fn futex( - uaddr: *const AtomicU32, - futex_op: c::c_int, - val: u32, - timeout: *const linux_raw_sys::general::__kernel_timespec, - uaddr2: *const AtomicU32, - val3: u32 - ) via SYS_futex -> c::c_long - } - - ret_usize(futex( - uaddr, - op as i32 | flags.bits() as i32, - val, - timeout.cast(), - uaddr2, - val3, - ) as isize) - } -} - -#[cfg(linux_kernel)] -pub(crate) unsafe fn futex_timeout( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32, -) -> io::Result { - #[cfg(all( - target_pointer_width = "32", - not(any(target_arch = "aarch64", target_arch = "x86_64")) - ))] - { - // TODO: Upstream this to the libc crate. - #[allow(non_upper_case_globals)] - const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; - - syscall! { - fn futex_time64( - uaddr: *const AtomicU32, - futex_op: c::c_int, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32 - ) via SYS_futex_time64 -> c::ssize_t - } - - ret_usize(futex_time64( - uaddr, - op as i32 | flags.bits() as i32, - val, - timeout, - uaddr2, - val3, - )) - .or_else(|err| { - // See the comments in `rustix_clock_gettime_via_syscall` about - // emulation. - if err == io::Errno::NOSYS { - futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) - } else { - Err(err) - } - }) - } - - #[cfg(any( - target_pointer_width = "64", - target_arch = "aarch64", - target_arch = "x86_64" - ))] - { - syscall! { - fn futex( - uaddr: *const AtomicU32, - futex_op: c::c_int, - val: u32, - timeout: *const linux_raw_sys::general::__kernel_timespec, - uaddr2: *const AtomicU32, - val3: u32 - ) via SYS_futex -> c::c_long - } - - ret_usize(futex( - uaddr, - op as i32 | flags.bits() as i32, - val, - timeout.cast(), - uaddr2, - val3, - ) as isize) - } -} - -#[cfg(linux_kernel)] -#[cfg(all( - target_pointer_width = "32", - not(any(target_arch = "aarch64", target_arch = "x86_64")) -))] -unsafe fn futex_old_timespec( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32, -) -> io::Result { - syscall! { - fn futex( - uaddr: *const AtomicU32, - futex_op: c::c_int, - val: u32, - timeout: *const linux_raw_sys::general::__kernel_old_timespec, - uaddr2: *const AtomicU32, - val3: u32 - ) via SYS_futex -> c::c_long - } - - let old_timeout = if timeout.is_null() { - None - } else { - Some(linux_raw_sys::general::__kernel_old_timespec { - tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*timeout) - .tv_nsec - .try_into() - .map_err(|_| io::Errno::INVAL)?, - }) - }; - ret_usize(futex( - uaddr, - op as i32 | flags.bits() as i32, - val, - old_timeout - .as_ref() - .map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec) - .unwrap_or(core::ptr::null()), - uaddr2, - val3, - ) as isize) -} - #[cfg(linux_kernel)] #[inline] pub(crate) fn setgroups_thread(groups: &[crate::ugid::Gid]) -> io::Result<()> { diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 5c0fd45c2..ea12f637b 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -789,20 +789,12 @@ 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, - )> for ArgReg<'a, Num> +#[cfg(feature = "futex")] +impl<'a, Num: ArgNumber> From<(crate::backend::futex::types::Operation, crate::futex::Flags)> + for ArgReg<'a, Num> { #[inline] - fn from( - pair: ( - crate::backend::thread::futex::FutexOperation, - crate::thread::FutexFlags, - ), - ) -> Self { + fn from(pair: (crate::backend::futex::types::Operation, crate::futex::Flags)) -> Self { c_uint(pair.0 as u32 | pair.1.bits()) } } diff --git a/src/backend/linux_raw/futex/mod.rs b/src/backend/linux_raw/futex/mod.rs new file mode 100644 index 000000000..1e0181a99 --- /dev/null +++ b/src/backend/linux_raw/futex/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/src/backend/linux_raw/futex/syscalls.rs b/src/backend/linux_raw/futex/syscalls.rs new file mode 100644 index 000000000..14a6b66b8 --- /dev/null +++ b/src/backend/linux_raw/futex/syscalls.rs @@ -0,0 +1,131 @@ +//! linux_raw syscalls supporting `rustix::futex`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::conv::{c_uint, ret_usize}; +use crate::futex; +use crate::io; +use crate::timespec::Timespec; +use core::sync::atomic::AtomicU32; +#[cfg(target_pointer_width = "32")] +use linux_raw_sys::general::timespec as __kernel_old_timespec; + +#[inline] +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, + op: super::types::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 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; + + #[cfg(target_pointer_width = "32")] + { + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) +} + +#[inline] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: super::types::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + #[cfg(target_pointer_width = "32")] + { + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) + .or_else(|err| { + // See the comments in `rustix_clock_gettime_via_syscall` about + // emulation. + if err == io::Errno::NOSYS { + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) + } else { + Err(err) + } + }) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) +} + +#[cfg(target_pointer_width = "32")] +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, + op: super::types::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + let old_timeout = if timeout.is_null() { + None + } else { + Some(__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) + }; + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + old_timeout + .as_ref() + .map(|timeout| timeout as *const __kernel_old_timespec) + .unwrap_or(core::ptr::null()), + uaddr2, + c_uint(val3) + )) +} diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/futex/types.rs similarity index 82% rename from src/backend/linux_raw/thread/futex.rs rename to src/backend/linux_raw/futex/types.rs index ed1a34a7e..3a1ce78a4 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/futex/types.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 + /// [`futex`]: crate::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,12 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. +/// `FUTEX_*` operations for use with the functions in [`futex`]. /// -/// [`futex`]: mod@crate::thread::futex +/// [`futex`]: crate::futex #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub enum Operation { /// `FUTEX_WAIT` Wait = linux_raw_sys::general::FUTEX_WAIT, /// `FUTEX_WAKE` @@ -53,7 +53,7 @@ pub enum FutexOperation { } /// `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/mod.rs b/src/backend/linux_raw/mod.rs index 0d4e5332d..8105eea3b 100644 --- a/src/backend/linux_raw/mod.rs +++ b/src/backend/linux_raw/mod.rs @@ -40,6 +40,9 @@ pub(crate) mod event; ) ))] pub(crate) mod fs; +#[cfg(linux_kernel)] +#[cfg(feature = "futex")] +pub(crate) mod futex; pub(crate) mod io; #[cfg(feature = "io_uring")] pub(crate) mod io_uring; diff --git a/src/backend/linux_raw/thread/mod.rs b/src/backend/linux_raw/thread/mod.rs index 6a002c632..ef944f04d 100644 --- a/src/backend/linux_raw/thread/mod.rs +++ b/src/backend/linux_raw/thread/mod.rs @@ -1,2 +1 @@ -pub(crate) mod futex; pub(crate) mod syscalls; diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 907bb6457..f6b734b43 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -5,18 +5,16 @@ //! 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, - slice_just_addr, slice_just_addr_mut, zero, + by_mut, by_ref, c_int, c_uint, ret, ret_c_int, ret_c_int_infallible, slice, slice_just_addr, + slice_just_addr_mut, zero, }; use crate::fd::BorrowedFd; use crate::io; use crate::pid::Pid; -use crate::thread::{ClockId, FutexFlags, NanosleepRelativeResult, Timespec}; +use crate::thread::{ClockId, NanosleepRelativeResult}; use core::mem::MaybeUninit; -use core::sync::atomic::AtomicU32; #[cfg(target_pointer_width = "32")] use linux_raw_sys::general::timespec as __kernel_old_timespec; use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; @@ -205,123 +203,6 @@ pub(crate) fn gettid() -> Pid { } } -#[inline] -pub(crate) unsafe fn futex_val2( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - 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 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; - - #[cfg(target_pointer_width = "32")] - { - ret_usize(syscall!( - __NR_futex_time64, - uaddr, - (op, flags), - c_uint(val), - timeout, - uaddr2, - c_uint(val3) - )) - } - #[cfg(target_pointer_width = "64")] - ret_usize(syscall!( - __NR_futex, - uaddr, - (op, flags), - c_uint(val), - timeout, - uaddr2, - c_uint(val3) - )) -} - -#[inline] -pub(crate) unsafe fn futex_timeout( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32, -) -> io::Result { - #[cfg(target_pointer_width = "32")] - { - ret_usize(syscall!( - __NR_futex_time64, - uaddr, - (op, flags), - c_uint(val), - timeout, - uaddr2, - c_uint(val3) - )) - .or_else(|err| { - // See the comments in `rustix_clock_gettime_via_syscall` about - // emulation. - if err == io::Errno::NOSYS { - futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) - } else { - Err(err) - } - }) - } - #[cfg(target_pointer_width = "64")] - ret_usize(syscall!( - __NR_futex, - uaddr, - (op, flags), - c_uint(val), - timeout, - uaddr2, - c_uint(val3) - )) -} - -#[cfg(target_pointer_width = "32")] -unsafe fn futex_old_timespec( - uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - timeout: *const Timespec, - uaddr2: *const AtomicU32, - val3: u32, -) -> io::Result { - let old_timeout = if timeout.is_null() { - None - } else { - Some(__kernel_old_timespec { - tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*timeout) - .tv_nsec - .try_into() - .map_err(|_| io::Errno::INVAL)?, - }) - }; - ret_usize(syscall!( - __NR_futex, - uaddr, - (op, flags), - c_uint(val), - old_timeout - .as_ref() - .map(|timeout| timeout as *const __kernel_old_timespec) - .unwrap_or(core::ptr::null()), - uaddr2, - 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/futex.rs similarity index 72% rename from src/thread/futex.rs rename to src/futex.rs index 1c89b96bb..82c8ed96d 100644 --- a/src/thread/futex.rs +++ b/src/futex.rs @@ -1,81 +1,36 @@ //! 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::syscalls::{futex_timeout, futex_val2}; +use crate::backend::futex::syscalls::{futex_timeout, futex_val2}; +use crate::backend::futex::types::Operation; use crate::fd::{FromRawFd, OwnedFd, RawFd}; -use crate::thread::Timespec; +use crate::futex::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::futex::types::Flags; /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = backend::thread::futex::FUTEX_WAITERS; +pub const WAITERS: u32 = backend::futex::types::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::futex::types::OWNER_DIED; /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// @@ -93,14 +48,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 +88,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 +106,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 +132,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 +156,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 +225,7 @@ pub enum WakeOpCmp { #[inline] pub fn wake_op( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, @@ -325,17 +241,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 +258,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 +295,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 +320,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 +342,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 +385,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 +418,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 +461,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 +483,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/lib.rs b/src/lib.rs index 28b77a96f..24896cbd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,10 @@ pub mod ffi; #[cfg(feature = "fs")] #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub mod fs; +#[cfg(linux_kernel)] +#[cfg(feature = "futex")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "futex")))] +pub mod futex; pub mod io; #[cfg(linux_kernel)] #[cfg(feature = "io_uring")] @@ -359,6 +363,7 @@ mod signal; #[cfg(not(windows))] #[cfg(any( feature = "fs", + feature = "futex", feature = "process", feature = "runtime", feature = "thread", diff --git a/src/thread/mod.rs b/src/thread/mod.rs index cb5d566f3..db84537c1 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -3,8 +3,6 @@ #[cfg(not(target_os = "redox"))] mod clock; #[cfg(linux_kernel)] -pub mod futex; -#[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] mod libcap; @@ -13,12 +11,15 @@ mod prctl; #[cfg(linux_kernel)] mod setns; +#[cfg(linux_kernel)] +pub use crate::backend::futex::types::Operation as FutexOperation; +#[cfg(linux_kernel)] +pub use crate::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 +30,61 @@ 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](`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 +#[cfg(linux_kernel)] +#[allow(unsafe_code)] +#[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, +) -> crate::io::Result { + use crate::backend::futex::syscalls::{futex_timeout, futex_val2}; + use core::sync::atomic::AtomicU32; + 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, + ), + } +} diff --git a/tests/thread/futex.rs b/tests/futex/basic.rs similarity index 72% rename from tests/thread/futex.rs rename to tests/futex/basic.rs index 918b7dac1..12705f7f3 100644 --- a/tests/thread/futex.rs +++ b/tests/futex/basic.rs @@ -1,22 +1,19 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use rustix::{ - io::Errno, - thread::{futex, FutexFlags}, -}; +use rustix::{futex, io::Errno}; #[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 +22,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 +35,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 +48,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 +63,13 @@ fn test_wait_wake() { #[cfg(feature = "std")] #[test] fn test_timeout() { - use rustix::fs::Timespec; + use rustix::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 +81,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: 0, @@ -96,7 +93,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: -1, diff --git a/tests/futex/main.rs b/tests/futex/main.rs new file mode 100644 index 000000000..322805c03 --- /dev/null +++ b/tests/futex/main.rs @@ -0,0 +1,6 @@ +//! Tests for [`rustix::futex`]. + +#![cfg(feature = "futex")] +#![cfg(linux_kernel)] + +mod basic; diff --git a/tests/thread/main.rs b/tests/thread/main.rs index bdd02dc6c..fccf2682c 100644 --- a/tests/thread/main.rs +++ b/tests/thread/main.rs @@ -6,8 +6,6 @@ #[cfg(not(target_os = "redox"))] mod clocks; #[cfg(linux_kernel)] -mod futex; -#[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] mod libcap;