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}