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
18#[derive(Debug, Copy, Clone, Eq, PartialEq)]
20pub struct FutexOpAndFlags {
21 op: FutexOp,
22 flags: FutexFlags,
23}
24
25impl FutexOpAndFlags {
26 pub fn new(op: FutexOp, flags: FutexFlags) -> Self {
27 Self { op, flags }
28 }
29
30 pub fn op(&self) -> FutexOp {
31 self.op
32 }
33
34 pub fn flags(&self) -> FutexFlags {
35 self.flags
36 }
37}
38
39#[derive(Debug, Copy, Clone, Eq, PartialEq)]
40pub enum FutexOpAndFlagsTryFromI32Error {
41 UnknownOp(i32),
42}
43
44impl TryFrom<i32> for FutexOpAndFlags {
45 type Error = FutexOpAndFlagsTryFromI32Error;
46 fn try_from(value: i32) -> Result<Self, Self::Error> {
47 let op_mask = 0b1111;
49 let op_value = value & op_mask;
50 let op = FutexOp::try_from(op_value)
51 .map_err(|_| FutexOpAndFlagsTryFromI32Error::UnknownOp(op_value))?;
52 let flags = FutexFlags::from_bits_retain(value & !op_mask);
53 Ok(FutexOpAndFlags::new(op, flags))
54 }
55}
56
57impl From<FutexOpAndFlags> for i32 {
58 fn from(value: FutexOpAndFlags) -> Self {
59 i32::from(value.op) | value.flags.bits()
60 }
61}
62
63bitflags::bitflags! {
64 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
66 pub struct FutexFlags: i32 {
67 const FUTEX_PRIVATE_FLAG = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_PRIVATE_FLAG);
68 const FUTEX_CLOCK_REALTIME = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CLOCK_REALTIME);
69 }
70}
71
72#[derive(
74 Debug, Copy, Clone, Eq, PartialEq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive,
75)]
76#[repr(i32)]
77#[allow(non_camel_case_types)]
78pub enum FutexOp {
79 FUTEX_WAIT = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT),
80 FUTEX_WAKE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE),
81 FUTEX_FD = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_FD),
82 FUTEX_REQUEUE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_REQUEUE),
83 FUTEX_CMP_REQUEUE = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CMP_REQUEUE),
84 FUTEX_WAKE_OP = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE_OP),
85 FUTEX_LOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_LOCK_PI),
86 FUTEX_UNLOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_UNLOCK_PI),
87 FUTEX_TRYLOCK_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_TRYLOCK_PI),
88 FUTEX_WAIT_BITSET = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT_BITSET),
89 FUTEX_WAKE_BITSET = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAKE_BITSET),
90 FUTEX_WAIT_REQUEUE_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_WAIT_REQUEUE_PI),
91 FUTEX_CMP_REQUEUE_PI = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_CMP_REQUEUE_PI),
92 FUTEX_LOCK_PI2 = const_conversions::i32_from_u32(bindings::LINUX_FUTEX_LOCK_PI2),
93}
94
95pub unsafe fn futex_raw(
98 uaddr: *mut u32,
99 op: core::ffi::c_int,
100 val: u32,
101 utime: *const kernel_timespec,
102 uaddr2: *mut u32,
103 val3: u32,
104) -> Result<core::ffi::c_int, Errno> {
105 unsafe {
106 syscall!(
107 linux_syscall::SYS_futex,
108 uaddr,
109 op,
110 val,
111 utime,
112 uaddr2,
113 val3
114 )
115 }
116 .try_i64()
117 .map(|x| x.try_into().expect("futex() returned invalid int"))
119 .map_err(Errno::from)
120}
121
122pub fn futex(
125 uaddr: &AtomicU32,
126 op: FutexOpAndFlags,
127 val: u32,
128 utime: Option<&kernel_timespec>,
129 uaddr2: Option<&AtomicU32>,
130 val3: u32,
131) -> Result<core::ffi::c_int, Errno> {
132 let utime = utime
133 .map(core::ptr::from_ref)
134 .unwrap_or(core::ptr::null_mut());
135 let uaddr2 = uaddr2
136 .map(AtomicU32::as_ptr)
137 .unwrap_or(core::ptr::null_mut());
138
139 unsafe { futex_raw(uaddr.as_ptr(), i32::from(op), val, utime, uaddr2, val3) }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[cfg(not(miri))]
148 #[test]
149 fn test_futex_error() {
150 let rv = unsafe {
151 futex_raw(
152 core::ptr::null_mut(),
153 0,
154 0,
155 core::ptr::null(),
156 core::ptr::null_mut(),
157 0,
158 )
159 };
160
161 assert_eq!(rv, Err(Errno::EFAULT));
163 }
164}