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