linux_api/
futex.rs

1use core::sync::atomic::AtomicU32;
2
3use linux_syscall::Result64 as LinuxSyscallResult64;
4use linux_syscall::syscall;
5
6use crate::errno::Errno;
7use crate::time::kernel_timespec;
8use crate::{bindings, const_conversions};
9
10pub use bindings::linux_robust_list_head;
11#[allow(non_camel_case_types)]
12pub type robust_list_head = linux_robust_list_head;
13unsafe impl shadow_pod::Pod for robust_list_head {}
14
15pub const FUTEX_CMD_MASK: i32 = bindings::LINUX_FUTEX_CMD_MASK;
16pub const FUTEX_BITSET_MATCH_ANY: u32 = bindings::LINUX_FUTEX_BITSET_MATCH_ANY;
17
18bitflags::bitflags! {
19    /// Flags that can be used in the `op` argument for the [`futex`] syscall.
20    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
21    pub struct FutexOpFlags: i32 {
22        const FUTEX_WAIT = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT);
23        const FUTEX_WAKE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE);
24        const FUTEX_FD = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_FD);
25        const FUTEX_REQUEUE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_REQUEUE);
26        const FUTEX_CMP_REQUEUE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CMP_REQUEUE);
27        const FUTEX_WAKE_OP = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE_OP);
28        const FUTEX_LOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_LOCK_PI);
29        const FUTEX_UNLOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_UNLOCK_PI);
30        const FUTEX_TRYLOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_TRYLOCK_PI);
31        const FUTEX_WAIT_BITSET = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT_BITSET);
32        const FUTEX_WAKE_BITSET = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE_BITSET);
33        const FUTEX_WAIT_REQUEUE_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT_REQUEUE_PI);
34        const FUTEX_CMP_REQUEUE_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CMP_REQUEUE_PI);
35        const FUTEX_LOCK_PI2 = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_LOCK_PI2);
36        const FUTEX_PRIVATE_FLAG = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_PRIVATE_FLAG);
37        const FUTEX_CLOCK_REALTIME = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CLOCK_REALTIME);
38    }
39}
40
41/// # Safety
42/// See futex(2). Pointers must be valid or NULL.
43pub unsafe fn futex_raw(
44    uaddr: *mut u32,
45    op: core::ffi::c_int,
46    val: u32,
47    utime: *const kernel_timespec,
48    uaddr2: *mut u32,
49    val3: u32,
50) -> Result<core::ffi::c_int, Errno> {
51    unsafe {
52        syscall!(
53            linux_syscall::SYS_futex,
54            uaddr,
55            op,
56            val,
57            utime,
58            uaddr2,
59            val3
60        )
61    }
62    .try_i64()
63    // the linux x86-64 syscall implementation returns an int so I don't think this should ever fail
64    .map(|x| x.try_into().expect("futex() returned invalid int"))
65    .map_err(Errno::from)
66}
67
68// I don't see any reason to mark this as "unsafe", but I didn't look through all of the possible
69// futex operations
70pub fn futex(
71    uaddr: &AtomicU32,
72    op: FutexOpFlags,
73    val: u32,
74    utime: Option<&kernel_timespec>,
75    uaddr2: Option<&AtomicU32>,
76    val3: u32,
77) -> Result<core::ffi::c_int, Errno> {
78    let utime = utime
79        .map(core::ptr::from_ref)
80        .unwrap_or(core::ptr::null_mut());
81    let uaddr2 = uaddr2
82        .map(AtomicU32::as_ptr)
83        .unwrap_or(core::ptr::null_mut());
84
85    unsafe { futex_raw(uaddr.as_ptr(), op.bits(), val, utime, uaddr2, val3) }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    // miri doesn't support non-libc syscalls
93    #[cfg(not(miri))]
94    #[test]
95    fn test_futex_error() {
96        let rv = unsafe {
97            futex_raw(
98                core::ptr::null_mut(),
99                0,
100                0,
101                core::ptr::null(),
102                core::ptr::null_mut(),
103                0,
104            )
105        };
106
107        // check that errors are returned correctly even though it returns a signed integer
108        assert_eq!(rv, Err(Errno::EFAULT));
109    }
110}