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. For a higher-level API that
5//! provides those abstractions, see [rustix-futex-sync].
6//!
7//! # Examples
8//!
9//! ```
10//! use rustix::thread::futex;
11//! use std::sync::atomic::AtomicU32;
12//!
13//! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> {
14//! // Wake up one waiter.
15//! futex::wake(futex, futex::Flags::PRIVATE, 1)?;
16//! # Ok(())
17//! # }
18//! ```
19//!
20//! # References
21//!  - [Linux `futex` system call]
22//!  - [Linux `futex` feature]
23//!
24//! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
25//! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
26//! [rustix-futex-sync]: https://crates.io/crates/rustix-futex-sync
27#![allow(unsafe_code)]
28
29use core::ffi::c_void;
30use core::num::NonZeroU32;
31use core::ptr;
32use core::sync::atomic::AtomicU32;
33
34use crate::backend::thread::futex::Operation;
35use crate::backend::thread::syscalls::{futex_timeout, futex_val2};
36use crate::fd::{FromRawFd as _, OwnedFd, RawFd};
37use crate::{backend, io};
38
39pub use crate::clockid::ClockId;
40pub use crate::timespec::{Nsecs, Secs, Timespec};
41
42pub use backend::thread::futex::{Flags, WaitFlags, OWNER_DIED, WAITERS};
43
44/// `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)`
45///
46/// This is a very low-level feature for implementing synchronization
47/// primitives. See the references links.
48///
49/// # References
50///  - [Linux `futex` system call]
51///  - [Linux `futex` feature]
52///
53/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
54/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
55#[inline]
56pub fn wait(
57    uaddr: &AtomicU32,
58    flags: Flags,
59    val: u32,
60    timeout: Option<&Timespec>,
61) -> io::Result<()> {
62    // SAFETY: The raw pointers come from references or null.
63    unsafe {
64        futex_timeout(uaddr, Operation::Wait, flags, val, timeout, ptr::null(), 0).map(|val| {
65            debug_assert_eq!(
66                val, 0,
67                "The return value should always equal zero, if the call is successful"
68            );
69        })
70    }
71}
72
73/// `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)`
74///
75/// This is a very low-level feature for implementing synchronization
76/// primitives. See the references links.
77///
78/// # References
79///  - [Linux `futex` system call]
80///  - [Linux `futex` feature]
81///
82/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
83/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
84#[inline]
85pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<usize> {
86    // SAFETY: The raw pointers come from references or null.
87    unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) }
88}
89
90/// `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)`
91///
92/// This is a very low-level feature for implementing synchronization
93/// primitives. See the references links.
94///
95/// # References
96///  - [Linux `futex` system call]
97///  - [Linux `futex` feature]
98///
99/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
100/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
101#[inline]
102pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result<OwnedFd> {
103    // SAFETY: The raw pointers come from references or null.
104    unsafe {
105        futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| {
106            let fd = val as RawFd;
107            debug_assert_eq!(fd as usize, val, "return value should be a valid fd");
108            OwnedFd::from_raw_fd(fd)
109        })
110    }
111}
112
113/// `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)`
114///
115/// This is a very low-level feature for implementing synchronization
116/// primitives. See the references links.
117///
118/// # References
119///  - [Linux `futex` system call]
120///  - [Linux `futex` feature]
121///
122/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
123/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
124#[inline]
125pub fn requeue(
126    uaddr: &AtomicU32,
127    flags: Flags,
128    val: u32,
129    val2: u32,
130    uaddr2: &AtomicU32,
131) -> io::Result<usize> {
132    // SAFETY: The raw pointers come from references or null.
133    unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) }
134}
135
136/// `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)`
137///
138/// This is a very low-level feature for implementing synchronization
139/// primitives. See the references links.
140///
141/// # References
142///  - [Linux `futex` system call]
143///  - [Linux `futex` feature]
144///
145/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
146/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
147#[inline]
148pub fn cmp_requeue(
149    uaddr: &AtomicU32,
150    flags: Flags,
151    val: u32,
152    val2: u32,
153    uaddr2: &AtomicU32,
154    val3: u32,
155) -> io::Result<usize> {
156    // SAFETY: The raw pointers come from references or null.
157    unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) }
158}
159
160/// `FUTEX_OP_*` operations for use with [`wake_op`].
161#[derive(Debug, Copy, Clone, Eq, PartialEq)]
162#[repr(u32)]
163#[allow(clippy::identity_op)]
164pub enum WakeOp {
165    /// `FUTEX_OP_SET`: `uaddr2 = oparg;`
166    Set = 0,
167    /// `FUTEX_OP_ADD`: `uaddr2 += oparg;`
168    Add = 1,
169    /// `FUTEX_OP_OR`: `uaddr2 |= oparg;`
170    Or = 2,
171    /// `FUTEX_OP_ANDN`: `uaddr2 &= ~oparg;`
172    AndN = 3,
173    /// `FUTEX_OP_XOR`: `uaddr2 ^= oparg;`
174    XOr = 4,
175    /// `FUTEX_OP_SET | FUTEX_OP_ARG_SHIFT`: `uaddr2 = (oparg << 1);`
176    SetShift = 0 | 8,
177    /// `FUTEX_OP_ADD | FUTEX_OP_ARG_SHIFT`: `uaddr2 += (oparg << 1);`
178    AddShift = 1 | 8,
179    /// `FUTEX_OP_OR | FUTEX_OP_ARG_SHIFT`: `uaddr2 |= (oparg << 1);`
180    OrShift = 2 | 8,
181    /// `FUTEX_OP_ANDN | FUTEX_OP_ARG_SHIFT`: `uaddr2 &= !(oparg << 1);`
182    AndNShift = 3 | 8,
183    /// `FUTEX_OP_XOR | FUTEX_OP_ARG_SHIFT`: `uaddr2 ^= (oparg << 1);`
184    XOrShift = 4 | 8,
185}
186
187/// `FUTEX_OP_CMP_*` operations for use with [`wake_op`].
188#[derive(Debug, Copy, Clone, Eq, PartialEq)]
189#[repr(u32)]
190pub enum WakeOpCmp {
191    /// `FUTEX_OP_CMP_EQ`: `if oldval == cmparg { wake(); }`
192    Eq = 0,
193    /// `FUTEX_OP_CMP_EQ`: `if oldval != cmparg { wake(); }`
194    Ne = 1,
195    /// `FUTEX_OP_CMP_EQ`: `if oldval < cmparg { wake(); }`
196    Lt = 2,
197    /// `FUTEX_OP_CMP_EQ`: `if oldval <= cmparg { wake(); }`
198    Le = 3,
199    /// `FUTEX_OP_CMP_EQ`: `if oldval > cmparg { wake(); }`
200    Gt = 4,
201    /// `FUTEX_OP_CMP_EQ`: `if oldval >= cmparg { wake(); }`
202    Ge = 5,
203}
204
205/// `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)`
206///
207/// This is a very low-level feature for implementing synchronization
208/// primitives. See the references links.
209///
210/// # References
211///  - [Linux `futex` system call]
212///  - [Linux `futex` feature]
213///
214/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
215/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
216#[inline]
217#[allow(clippy::too_many_arguments)]
218pub fn wake_op(
219    uaddr: &AtomicU32,
220    flags: Flags,
221    val: u32,
222    val2: u32,
223    uaddr2: &AtomicU32,
224    op: WakeOp,
225    cmp: WakeOpCmp,
226    oparg: u16,
227    cmparg: u16,
228) -> io::Result<usize> {
229    if oparg >= 1 << 12 || cmparg >= 1 << 12 {
230        return Err(io::Errno::INVAL);
231    }
232
233    let val3 =
234        ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32);
235
236    // SAFETY: The raw pointers come from references or null.
237    unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) }
238}
239
240/// `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)`
241///
242/// This is a very low-level feature for implementing synchronization
243/// primitives. See the references links.
244///
245/// # References
246///  - [Linux `futex` system call]
247///  - [Linux `futex` feature]
248///
249/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
250/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
251#[inline]
252pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option<&Timespec>) -> io::Result<()> {
253    // SAFETY: The raw pointers come from references or null.
254    unsafe {
255        futex_timeout(uaddr, Operation::LockPi, flags, 0, timeout, ptr::null(), 0).map(|val| {
256            debug_assert_eq!(
257                val, 0,
258                "The return value should always equal zero, if the call is successful"
259            );
260        })
261    }
262}
263
264/// `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)`
265///
266/// This is a very low-level feature for implementing synchronization
267/// primitives. See the references links.
268///
269/// # References
270///  - [Linux `futex` system call]
271///  - [Linux `futex` feature]
272///
273/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
274/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
275#[inline]
276pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> {
277    // SAFETY: The raw pointers come from references or null.
278    unsafe {
279        futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| {
280            debug_assert_eq!(
281                val, 0,
282                "The return value should always equal zero, if the call is successful"
283            );
284        })
285    }
286}
287
288/// `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)`
289///
290/// This is a very low-level feature for implementing synchronization
291/// primitives. See the references links.
292///
293/// # References
294///  - [Linux `futex` system call]
295///  - [Linux `futex` feature]
296///
297/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
298/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
299#[inline]
300pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<bool> {
301    // SAFETY: The raw pointers come from references or null.
302    unsafe {
303        futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0)
304    }
305}
306
307/// `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)`
308///
309/// This is a very low-level feature for implementing synchronization
310/// primitives. See the references links.
311///
312/// # References
313///  - [Linux `futex` system call]
314///  - [Linux `futex` feature]
315///
316/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
317/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
318#[inline]
319pub fn wait_bitset(
320    uaddr: &AtomicU32,
321    flags: Flags,
322    val: u32,
323    timeout: Option<&Timespec>,
324    val3: NonZeroU32,
325) -> io::Result<()> {
326    // SAFETY: The raw pointers come from references or null.
327    unsafe {
328        futex_timeout(
329            uaddr,
330            Operation::WaitBitset,
331            flags,
332            val,
333            timeout,
334            ptr::null(),
335            val3.get(),
336        )
337        .map(|val| {
338            debug_assert_eq!(
339                val, 0,
340                "The return value should always equal zero, if the call is successful"
341            );
342        })
343    }
344}
345
346/// `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)`
347///
348/// This is a very low-level feature for implementing synchronization
349/// primitives. See the references links.
350///
351/// # References
352///  - [Linux `futex` system call]
353///  - [Linux `futex` feature]
354///
355/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
356/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
357#[inline]
358pub fn wake_bitset(
359    uaddr: &AtomicU32,
360    flags: Flags,
361    val: u32,
362    val3: NonZeroU32,
363) -> io::Result<usize> {
364    // SAFETY: The raw pointers come from references or null.
365    unsafe {
366        futex_val2(
367            uaddr,
368            Operation::WakeBitset,
369            flags,
370            val,
371            0,
372            ptr::null(),
373            val3.get(),
374        )
375    }
376}
377
378/// `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)`
379///
380/// This is a very low-level feature for implementing synchronization
381/// primitives. See the references links.
382///
383/// # References
384///  - [Linux `futex` system call]
385///  - [Linux `futex` feature]
386///
387/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
388/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
389#[inline]
390pub fn wait_requeue_pi(
391    uaddr: &AtomicU32,
392    flags: Flags,
393    val: u32,
394    timeout: Option<&Timespec>,
395    uaddr2: &AtomicU32,
396) -> io::Result<()> {
397    // SAFETY: The raw pointers come from references or null.
398    unsafe {
399        futex_timeout(
400            uaddr,
401            Operation::WaitRequeuePi,
402            flags,
403            val,
404            timeout,
405            uaddr2,
406            0,
407        )
408        .map(|val| {
409            debug_assert_eq!(
410                val, 0,
411                "The return value should always equal zero, if the call is successful"
412            );
413        })
414    }
415}
416
417/// `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)`
418///
419/// This is a very low-level feature for implementing synchronization
420/// primitives. See the references links.
421///
422/// # References
423///  - [Linux `futex` system call]
424///  - [Linux `futex` feature]
425///
426/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
427/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
428#[inline]
429pub fn cmp_requeue_pi(
430    uaddr: &AtomicU32,
431    flags: Flags,
432    val2: u32,
433    uaddr2: &AtomicU32,
434    val3: u32,
435) -> io::Result<usize> {
436    // SAFETY: The raw pointers come from references or null.
437    unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) }
438}
439
440/// `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)`
441///
442/// This is a very low-level feature for implementing synchronization
443/// primitives. See the references links.
444///
445/// # References
446///  - [Linux `futex` system call]
447///  - [Linux `futex` feature]
448///
449/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html
450/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html
451#[inline]
452pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option<&Timespec>) -> io::Result<()> {
453    // SAFETY: The raw pointers come from references or null.
454    unsafe {
455        futex_timeout(uaddr, Operation::LockPi2, flags, 0, timeout, ptr::null(), 0).map(|val| {
456            debug_assert_eq!(
457                val, 0,
458                "The return value should always equal zero, if the call is successful"
459            );
460        })
461    }
462}
463
464/// A pointer in the [`Wait`] struct.
465#[repr(C)]
466#[derive(Copy, Clone)]
467#[non_exhaustive]
468pub struct WaitPtr {
469    #[cfg(all(target_pointer_width = "32", target_endian = "big"))]
470    #[doc(hidden)]
471    pub __pad32: u32,
472    #[cfg(all(target_pointer_width = "16", target_endian = "big"))]
473    #[doc(hidden)]
474    pub __pad16: u16,
475
476    /// The pointer value.
477    pub ptr: *mut c_void,
478
479    #[cfg(all(target_pointer_width = "16", target_endian = "little"))]
480    #[doc(hidden)]
481    pub __pad16: u16,
482    #[cfg(all(target_pointer_width = "32", target_endian = "little"))]
483    #[doc(hidden)]
484    pub __pad32: u32,
485}
486
487impl WaitPtr {
488    /// Construct a new `WaitPtr` holding the given raw pointer value.
489    #[inline]
490    pub const fn new(ptr: *mut c_void) -> Self {
491        Self {
492            ptr,
493
494            #[cfg(target_pointer_width = "16")]
495            __pad16: 0,
496            #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
497            __pad32: 0,
498        }
499    }
500}
501
502impl Default for WaitPtr {
503    #[inline]
504    fn default() -> Self {
505        Self::new(ptr::null_mut())
506    }
507}
508
509impl From<*mut c_void> for WaitPtr {
510    #[inline]
511    fn from(ptr: *mut c_void) -> Self {
512        Self::new(ptr)
513    }
514}
515
516impl core::fmt::Debug for WaitPtr {
517    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
518        self.ptr.fmt(f)
519    }
520}
521
522/// For use with [`waitv`].
523#[repr(C)]
524#[derive(Debug, Copy, Clone)]
525#[non_exhaustive]
526pub struct Wait {
527    /// The expected value.
528    pub val: u64,
529    /// The address to wait for.
530    pub uaddr: WaitPtr,
531    /// The type and size of futex to perform.
532    pub flags: WaitFlags,
533
534    /// Reserved for future use.
535    pub(crate) __reserved: u32,
536}
537
538impl Wait {
539    /// Construct a zero-initialized `Wait`.
540    #[inline]
541    pub const fn new() -> Self {
542        Self {
543            val: 0,
544            uaddr: WaitPtr::new(ptr::null_mut()),
545            flags: WaitFlags::empty(),
546            __reserved: 0,
547        }
548    }
549}
550
551impl Default for Wait {
552    #[inline]
553    fn default() -> Self {
554        Self::new()
555    }
556}
557
558/// `futex_waitv(waiters.as_ptr(), waiters.len(), flags, timeout, clockd)`—
559/// Wait on an array of futexes, wake on any.
560///
561/// This requires Linux ≥ 5.16.
562///
563/// # References
564///  - [Linux]
565///
566/// [Linux]: https://www.kernel.org/doc/html/latest/userspace-api/futex2.html
567#[inline]
568pub fn waitv(
569    waiters: &[Wait],
570    flags: WaitvFlags,
571    timeout: Option<&Timespec>,
572    clockid: ClockId,
573) -> io::Result<usize> {
574    backend::thread::syscalls::futex_waitv(waiters, flags, timeout, clockid)
575}
576
577bitflags::bitflags! {
578    /// Flags for use with the flags argument in [`waitv`].
579    ///
580    /// At this time, no flags are defined.
581    #[repr(transparent)]
582    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
583    pub struct WaitvFlags: u32 {
584        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
585        const _ = !0;
586    }
587}
588
589#[cfg(linux_raw)]
590#[cfg(test)]
591mod tests {
592    use super::*;
593
594    #[test]
595    fn test_layouts() {
596        use crate::backend::c;
597
598        check_renamed_struct!(Wait, futex_waitv, val, uaddr, flags, __reserved);
599    }
600}