Skip to content

Commit

Permalink
Move futex to be a top-level module.
Browse files Browse the repository at this point in the history
With `rustix::thread::futex` being both a deprecated function and
a public module, I'm seeing warnings like this when I use it in
some simple testcases:

```console
warning: use of deprecated function `rustix::thread::futex`: There are now individual functions available to perform futex operations with improved type safety. See the futex module.
 --> test.rs:2:21
  |
2 | use rustix::thread::futex;
  |                     ^^^^^
  |
  = note: `#[warn(deprecated)]` on by default
```

I think we can fix this by moving `futex` to be a top-level module,
at `rustix::futex`. And this also reflects that, strictly speaking,
futexes aren't specific to threads and can be used between processes
too.

Of course, this leaves the deprecated `rustix::thread::futex` and
the associated types and constants in place.

Also while using the API, I found `futex::FutexFlags` to be a little
redundant, so I think it makes sense to rename it to `futex::Flags`,
and similarly rename `futex::FUTEX_WAITERS` to `futex::WAITERS` and
so on.
  • Loading branch information
sunfishcode committed Aug 26, 2024
1 parent 777fbc6 commit 4f66307
Show file tree
Hide file tree
Showing 21 changed files with 513 additions and 553 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand All @@ -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"]
Expand Down Expand Up @@ -190,6 +193,7 @@ runtime = ["linux-raw-sys/prctl"]
all-apis = [
"event",
"fs",
"futex",
"io_uring",
"mm",
"mount",
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/backend/libc/futex/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;
202 changes: 202 additions & 0 deletions src/backend/libc/futex/syscalls.rs
Original file line number Diff line number Diff line change
@@ -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<usize> {
// 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<usize> {
#[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<usize> {
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)
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
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`
const CLOCK_REALTIME = bitcast!(c::FUTEX_CLOCK_REALTIME);
}
}

/// `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`
Expand Down Expand Up @@ -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;
3 changes: 3 additions & 0 deletions src/backend/libc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 0 additions & 2 deletions src/backend/libc/thread/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#[cfg(linux_kernel)]
pub(crate) mod futex;
#[cfg(not(windows))]
pub(crate) mod syscalls;
Loading

0 comments on commit 4f66307

Please sign in to comment.