rustix/thread/
futex.rs

1//! Linux `futex`.
2//!
3//! Futex is a very low-level mechanism for implementing concurrency primitives
4//! such as mutexes, rwlocks, and condvars.
5//!
6//! # Examples
7//!
8//! ```
9//! use rustix::thread::futex;
10//! use std::sync::atomic::AtomicU32;
11//!
12//! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> {
13//! // Wake up one waiter.
14//! futex::wake(futex, futex::Flags::PRIVATE, 1)?;
15//! # Ok(())
16//! # }
17//! ```
18//!
19//! # References
20//!  - [Linux `futex` system call]
21//!  - [Linux `futex` feature]
22//!
23//! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
24//! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
25#![allow(unsafe_code)]
26
27use core::num::NonZeroU32;
28use core::ptr;
29use core::sync::atomic::AtomicU32;
30
31use crate::backend::thread::futex::Operation;
32use crate::backend::thread::syscalls::{futex_timeout, futex_val2};
33use crate::fd::{FromRawFd, OwnedFd, RawFd};
34use crate::utils::option_as_ptr;
35use crate::{backend, io};
36
37pub use crate::timespec::{Nsecs, Secs, Timespec};
38
39pub use backend::thread::futex::{Flags, OWNER_DIED, WAITERS};
40
41/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)`
42///
43/// This is a very low-level feature for implementing synchronization
44/// primitives. See the references links.
45///
46/// # References
47///  - [Linux `futex` system call]
48///  - [Linux `futex` feature]
49///
50/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
51/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
52#[inline]
53pub fn wait(
54    uaddr: &AtomicU32,
55    flags: Flags,
56    val: u32,
57    timeout: Option<Timespec>,
58) -> io::Result<()> {
59    // SAFETY: The raw pointers come from references or null.
60    unsafe {
61        futex_timeout(
62            uaddr,
63            Operation::Wait,
64            flags,
65            val,
66            option_as_ptr(timeout.as_ref()),
67            ptr::null(),
68            0,
69        )
70        .map(|val| {
71            debug_assert_eq!(
72                val, 0,
73                "The return value should always equal zero, if the call is successful"
74            );
75        })
76    }
77}
78
79/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)`
80///
81/// This is a very low-level feature for implementing synchronization
82/// primitives. See the references links.
83///
84/// # References
85///  - [Linux `futex` system call]
86///  - [Linux `futex` feature]
87///
88/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
89/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
90#[inline]
91pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<usize> {
92    // SAFETY: The raw pointers come from references or null.
93    unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) }
94}
95
96/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)`
97///
98/// This is a very low-level feature for implementing synchronization
99/// primitives. See the references links.
100///
101/// # References
102///  - [Linux `futex` system call]
103///  - [Linux `futex` feature]
104///
105/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
106/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
107#[inline]
108pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<OwnedFd> {
109    // SAFETY: The raw pointers come from references or null.
110    unsafe {
111        futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| {
112            let fd = val as RawFd;
113            debug_assert_eq!(fd as usize, val, "return value should be a valid fd");
114            OwnedFd::from_raw_fd(fd)
115        })
116    }
117}
118
119/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)`
120///
121/// This is a very low-level feature for implementing synchronization
122/// primitives. See the references links.
123///
124/// # References
125///  - [Linux `futex` system call]
126///  - [Linux `futex` feature]
127///
128/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
129/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
130#[inline]
131pub fn requeue(
132    uaddr: &AtomicU32,
133    flags: Flags,
134    val: u32,
135    val2: u32,
136    uaddr2: &AtomicU32,
137) -> io::Result<usize> {
138    // SAFETY: The raw pointers come from references or null.
139    unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) }
140}
141
142/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)`
143///
144/// This is a very low-level feature for implementing synchronization
145/// primitives. See the references links.
146///
147/// # References
148///  - [Linux `futex` system call]
149///  - [Linux `futex` feature]
150///
151/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
152/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
153#[inline]
154pub fn cmp_requeue(
155    uaddr: &AtomicU32,
156    flags: Flags,
157    val: u32,
158    val2: u32,
159    uaddr2: &AtomicU32,
160    val3: u32,
161) -> io::Result<usize> {
162    // SAFETY: The raw pointers come from references or null.
163    unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) }
164}
165
166/// `FUTEX_OP_*` operations for use with [`wake_op`].
167#[derive(Debug, Copy, Clone, Eq, PartialEq)]
168#[repr(u32)]
169#[allow(clippy::identity_op)]
170pub enum WakeOp {
171    /// `FUTEX_OP_SET`: `uaddr2 = oparg;`
172    Set = 0,
173    /// `FUTEX_OP_ADD`: `uaddr2 += oparg;`
174    Add = 1,
175    /// `FUTEX_OP_OR`: `uaddr2 |= oparg;`
176    Or = 2,
177    /// `FUTEX_OP_ANDN`: `uaddr2 &= ~oparg;`
178    AndN = 3,
179    /// `FUTEX_OP_XOR`: `uaddr2 ^= oparg;`
180    XOr = 4,
181    /// `FUTEX_OP_SET | FUTEX_OP_ARG_SHIFT`: `uaddr2 = (oparg << 1);`
182    SetShift = 0 | 8,
183    /// `FUTEX_OP_ADD | FUTEX_OP_ARG_SHIFT`: `uaddr2 += (oparg << 1);`
184    AddShift = 1 | 8,
185    /// `FUTEX_OP_OR | FUTEX_OP_ARG_SHIFT`: `uaddr2 |= (oparg << 1);`
186    OrShift = 2 | 8,
187    /// `FUTEX_OP_ANDN | FUTEX_OP_ARG_SHIFT`: `uaddr2 &= !(oparg << 1);`
188    AndNShift = 3 | 8,
189    /// `FUTEX_OP_XOR | FUTEX_OP_ARG_SHIFT`: `uaddr2 ^= (oparg << 1);`
190    XOrShift = 4 | 8,
191}
192
193/// `FUTEX_OP_CMP_*` operations for use with [`wake_op`].
194#[derive(Debug, Copy, Clone, Eq, PartialEq)]
195#[repr(u32)]
196pub enum WakeOpCmp {
197    /// `FUTEX_OP_CMP_EQ`: `if oldval == cmparg { wake(); }`
198    Eq = 0,
199    /// `FUTEX_OP_CMP_EQ`: `if oldval != cmparg { wake(); }`
200    Ne = 1,
201    /// `FUTEX_OP_CMP_EQ`: `if oldval < cmparg { wake(); }`
202    Lt = 2,
203    /// `FUTEX_OP_CMP_EQ`: `if oldval <= cmparg { wake(); }`
204    Le = 3,
205    /// `FUTEX_OP_CMP_EQ`: `if oldval > cmparg { wake(); }`
206    Gt = 4,
207    /// `FUTEX_OP_CMP_EQ`: `if oldval >= cmparg { wake(); }`
208    Ge = 5,
209}
210
211/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)`
212///
213/// This is a very low-level feature for implementing synchronization
214/// primitives. See the references links.
215///
216/// # References
217///  - [Linux `futex` system call]
218///  - [Linux `futex` feature]
219///
220/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
221/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
222#[inline]
223#[allow(clippy::too_many_arguments)]
224pub fn wake_op(
225    uaddr: &AtomicU32,
226    flags: Flags,
227    val: u32,
228    val2: u32,
229    uaddr2: &AtomicU32,
230    op: WakeOp,
231    cmp: WakeOpCmp,
232    oparg: u16,
233    cmparg: u16,
234) -> io::Result<usize> {
235    if oparg >= 1 << 12 || cmparg >= 1 << 12 {
236        return Err(io::Errno::INVAL);
237    }
238
239    let val3 =
240        ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32);
241
242    // SAFETY: The raw pointers come from references or null.
243    unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) }
244}
245
246/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)`
247///
248/// This is a very low-level feature for implementing synchronization
249/// primitives. See the references links.
250///
251/// # References
252///  - [Linux `futex` system call]
253///  - [Linux `futex` feature]
254///
255/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
256/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
257#[inline]
258pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option<Timespec>) -> io::Result<()> {
259    // SAFETY: The raw pointers come from references or null.
260    unsafe {
261        futex_timeout(
262            uaddr,
263            Operation::LockPi,
264            flags,
265            0,
266            option_as_ptr(timeout.as_ref()),
267            ptr::null(),
268            0,
269        )
270        .map(|val| {
271            debug_assert_eq!(
272                val, 0,
273                "The return value should always equal zero, if the call is successful"
274            );
275        })
276    }
277}
278
279/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)`
280///
281/// This is a very low-level feature for implementing synchronization
282/// primitives. See the references links.
283///
284/// # References
285///  - [Linux `futex` system call]
286///  - [Linux `futex` feature]
287///
288/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
289/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
290#[inline]
291pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> {
292    // SAFETY: The raw pointers come from references or null.
293    unsafe {
294        futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| {
295            debug_assert_eq!(
296                val, 0,
297                "The return value should always equal zero, if the call is successful"
298            );
299        })
300    }
301}
302
303/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)`
304///
305/// This is a very low-level feature for implementing synchronization
306/// primitives. See the references links.
307///
308/// # References
309///  - [Linux `futex` system call]
310///  - [Linux `futex` feature]
311///
312/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
313/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
314#[inline]
315pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<bool> {
316    // SAFETY: The raw pointers come from references or null.
317    unsafe {
318        futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0)
319    }
320}
321
322/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)`
323///
324/// This is a very low-level feature for implementing synchronization
325/// primitives. See the references links.
326///
327/// # References
328///  - [Linux `futex` system call]
329///  - [Linux `futex` feature]
330///
331/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
332/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
333#[inline]
334pub fn wait_bitset(
335    uaddr: &AtomicU32,
336    flags: Flags,
337    val: u32,
338    timeout: Option<Timespec>,
339    val3: NonZeroU32,
340) -> io::Result<()> {
341    // SAFETY: The raw pointers come from references or null.
342    unsafe {
343        futex_timeout(
344            uaddr,
345            Operation::WaitBitset,
346            flags,
347            val,
348            option_as_ptr(timeout.as_ref()),
349            ptr::null(),
350            val3.get(),
351        )
352        .map(|val| {
353            debug_assert_eq!(
354                val, 0,
355                "The return value should always equal zero, if the call is successful"
356            );
357        })
358    }
359}
360
361/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)`
362///
363/// This is a very low-level feature for implementing synchronization
364/// primitives. See the references links.
365///
366/// # References
367///  - [Linux `futex` system call]
368///  - [Linux `futex` feature]
369///
370/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
371/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
372#[inline]
373pub fn wake_bitset(
374    uaddr: &AtomicU32,
375    flags: Flags,
376    val: u32,
377    val3: NonZeroU32,
378) -> io::Result<usize> {
379    // SAFETY: The raw pointers come from references or null.
380    unsafe {
381        futex_val2(
382            uaddr,
383            Operation::WakeBitset,
384            flags,
385            val,
386            0,
387            ptr::null(),
388            val3.get(),
389        )
390    }
391}
392
393/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)`
394///
395/// This is a very low-level feature for implementing synchronization
396/// primitives. See the references links.
397///
398/// # References
399///  - [Linux `futex` system call]
400///  - [Linux `futex` feature]
401///
402/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
403/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
404#[inline]
405pub fn wait_requeue_pi(
406    uaddr: &AtomicU32,
407    flags: Flags,
408    val: u32,
409    timeout: Option<Timespec>,
410    uaddr2: &AtomicU32,
411) -> io::Result<()> {
412    // SAFETY: The raw pointers come from references or null.
413    unsafe {
414        futex_timeout(
415            uaddr,
416            Operation::WaitRequeuePi,
417            flags,
418            val,
419            option_as_ptr(timeout.as_ref()),
420            uaddr2,
421            0,
422        )
423        .map(|val| {
424            debug_assert_eq!(
425                val, 0,
426                "The return value should always equal zero, if the call is successful"
427            );
428        })
429    }
430}
431
432/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)`
433///
434/// This is a very low-level feature for implementing synchronization
435/// primitives. See the references links.
436///
437/// # References
438///  - [Linux `futex` system call]
439///  - [Linux `futex` feature]
440///
441/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
442/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
443#[inline]
444pub fn cmp_requeue_pi(
445    uaddr: &AtomicU32,
446    flags: Flags,
447    val2: u32,
448    uaddr2: &AtomicU32,
449    val3: u32,
450) -> io::Result<usize> {
451    // SAFETY: The raw pointers come from references or null.
452    unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) }
453}
454
455/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)`
456///
457/// This is a very low-level feature for implementing synchronization
458/// primitives. See the references links.
459///
460/// # References
461///  - [Linux `futex` system call]
462///  - [Linux `futex` feature]
463///
464/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
465/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
466#[inline]
467pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option<Timespec>) -> io::Result<()> {
468    // SAFETY: The raw pointers come from references or null.
469    unsafe {
470        futex_timeout(
471            uaddr,
472            Operation::LockPi2,
473            flags,
474            0,
475            option_as_ptr(timeout.as_ref()),
476            ptr::null(),
477            0,
478        )
479        .map(|val| {
480            debug_assert_eq!(
481                val, 0,
482                "The return value should always equal zero, if the call is successful"
483            );
484        })
485    }
486}