Skip to main content

air_sys_types/
futex.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Types de la famille `futex` — primitive de blocage/réveil noyau (`futex(2)`).
6//!
7//! Cf. `docs/specs/layer-0/family-futex.md` (ADR-048, re-sceau `couche-0-v1.6`).
8//!
9//! La famille `futex(2)` classique (syscall `SYS_futex`) sous-tend les briques de
10//! synchronisation `std`-free de la libc Air (`air-thread`, couche 1) **et** le
11//! mutex interne de l'état io_uring partagé de la couche 0. Le mot futex est un
12//! [`core::sync::atomic::AtomicU32`] manipulé par l'appelant ; ces types décrivent
13//! les deux attributs **typés** des opérations (cf. ADR-021 §3 : pas de drapeau
14//! d'`op` brut, chaque attribut est un type) :
15//!
16//! - [`FutexScope`] : portée process-privée (cas courant) vs partagée inter-process.
17//! - [`FutexWakeCount`] : nombre de waiters à réveiller, borné par construction à
18//!   `INT_MAX` (le `val` de `FUTEX_WAKE` est un `int` côté noyau).
19//!
20//! Les wrappers `futex_wait`/`futex_wake` qui consomment ces types vivent dans
21//! `air-sys-syscall::futex`.
22
23/// Portée d'une opération `futex(2)` : **process-privée** ou **partagée**.
24///
25/// Le noyau distingue deux familles de futex via le drapeau `FUTEX_PRIVATE_FLAG` :
26///
27/// - [`FutexScope::Private`] (`FUTEX_*_PRIVATE`) — le **cas courant**, et le
28///   **défaut** ([`Default`]). Le futex n'est partagé qu'entre les threads d'un
29///   **même** processus ; le noyau saute la résolution d'adresse inter-process
30///   (clé futex basée sur l'adresse virtuelle, pas sur l'inode du mapping) — c'est
31///   strictement plus rapide. C'est la portée d'un `Mutex`/`Condvar` intra-processus.
32/// - [`FutexScope::Shared`] — le mot futex vit dans de la mémoire **partagée**
33///   (`mmap(MAP_SHARED)`, segment SysV…) et synchronise des **processus distincts**.
34///   Le noyau identifie le futex par l'inode + offset du mapping sous-jacent.
35///
36/// Exposer la portée comme un **type** (et non un drapeau d'`op` brut) respecte
37/// ADR-021 §3 : `futex_wait`/`futex_wake` restent **une fonction typée par
38/// opération**, la portée est un paramètre typé orthogonal.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
40pub enum FutexScope {
41    /// Futex **process-privé** (`FUTEX_*_PRIVATE`) — défaut, cas courant.
42    #[default]
43    Private,
44    /// Futex **partagé** entre processus (mémoire `MAP_SHARED`).
45    Shared,
46}
47
48/// Nombre de waiters à réveiller par `futex_wake` (`val` de `FUTEX_WAKE`).
49///
50/// Le `val` de `FUTEX_WAKE` est interprété comme un `int` (signé 32 bits) par le
51/// noyau : seules les valeurs `[0, INT_MAX]` ont un sens. Ce newtype **borne par
52/// construction** (« parse, don't validate », Principe 4) la valeur à
53/// `INT_MAX` (`0x7fff_ffff`) — [`FutexWakeCount::new`] **clampe** toute valeur plus
54/// grande. Le wrapper syscall n'a donc jamais à gérer une conversion qui échoue.
55///
56/// Réveiller `INT_MAX` waiters équivaut à « réveiller **tous** les waiters » :
57/// c'est l'idiome [`FutexWakeCount::ALL`].
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
59pub struct FutexWakeCount(u32);
60
61impl FutexWakeCount {
62    /// Borne haute : `INT_MAX` (`0x7fff_ffff`). Le `val` de `FUTEX_WAKE` est un
63    /// `int` signé côté noyau ; au-delà la valeur deviendrait négative.
64    const INT_MAX: u32 = 0x7fff_ffff;
65
66    /// Réveille **un seul** waiter (le cas d'un `Mutex` qui relâche son verrou).
67    pub const ONE: Self = Self(1);
68
69    /// Réveille **tous** les waiters (valeur noyau `INT_MAX`) — le cas d'un
70    /// `Condvar::notify_all` ou d'un barrage qui s'ouvre.
71    pub const ALL: Self = Self(Self::INT_MAX);
72
73    /// Construit un compte de réveil, **clampé** à `INT_MAX` (Principe 4 :
74    /// l'invariant `≤ INT_MAX` est garanti par construction, jamais revalidé).
75    #[must_use]
76    pub const fn new(count: u32) -> Self {
77        if count > Self::INT_MAX {
78            Self::ALL
79        } else {
80            Self(count)
81        }
82    }
83
84    /// Valeur brute (`u32`), garantie dans `[0, INT_MAX]`.
85    #[must_use]
86    pub const fn get(self) -> u32 {
87        self.0
88    }
89}
90
91#[cfg(test)]
92mod tests;