Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audit face-PAL — clôture pré-sceau couche-1-v1.0 (roadmap §5.2)

Statut : note d’audit (completeness critic), NON engageante. Préalable au sceau couche-1-v1.0 (roadmap §5.2). Lecture seule du code — aucune modification. Symboles réels, grepés (crates/), pas devinés. Rédigé le 2026-07-02.

Objet. Le PAL Rust = le backend std::sys qu’une toolchain *-linux-air (ADR-049/050/052) doit implémenter. Cet audit statue, primitive std::sys par primitive, si la brique couche 1 (API Rust) existe pour porter la primitive. Le PAL est un toit au-dessus de la couche 1 (ADR-052 : seule la couche 1 consomme la couche 0) : il compose les briques couche 1 ; il ne re-syscall pas.

Grille de statut.Brique présente · ◐ Partielle (manque précisé) · ❌ Manquante. Pour ◐/❌ : BLOQUANT (fondateur absent — non comblable sans refonte d’une API qu’on s’apprête à sceller) vs ADDITIF post-sceau (comblable en v1.x par descellement additif, sans toucher l’API scellée — modèle ADR-051).


1. Tableau — primitive std::sys → brique(s) couche 1 → statut

#Primitive std::sysBrique(s) couche 1 (symboles réels)StatutBloquant / Additif
1thread — spawn/joinair_runtime::thread::spawn(entry: extern "C" fn(usize), arg: usize) -> Result<JoinHandle, Errno> + JoinHandle::{join, tid} (thread.rs:136/85) ; primitive : air_thread::runtime_primitives::create_thread(...) (runtime_primitives.rs:147)Additif — le spawn natif bas-niveau (pile mmap + TLS/TCB + clone3 + join futex) est présent et prouvé on-target. L’ergonomique boxé (FnOnce()->T) manque sur la cible → §2.B.2 ci-dessous.
1bthread — yield / sleep / name / idyield : couche 0 air_sys_syscall::process::sched_yield() (process.rs:3494) ; sleep : air_base_core::time::{sleep, sleep_until} (time.rs:331/359) ; id : air_thread::runtime_primitives::current_tid() (:264), air_runtime::AirRuntime::current_tid() ; name (PR_SET_NAME) : cible absent (seul le wrapper hôte std::thread)Additif — sleep/yield/id ✅ ; yield_now = passthrough trivial sched_yield (couche 0) à surfacer ; nommage de thread sur cible = wrapper prctl(PR_SET_NAME) à ajouter.
2sync — Mutex / RwLockair_thread::{AirMutex, AirRwLock} (+ guards) sur futex(2) maison (sync.rs:135/341), sans std::sync, sans poisoning, modélisés loom (ADR-058)— (fondateur présent)
2bsync — Condvar(aucun type Condvar/AirCondvar) — briques futex : air_thread::runtime_primitives::{futex_wait_word, futex_wake_one, reset_futex_word} (:219/238/248)Additif — pas de brique Condvar, mais les primitives futex fondatrices sont exposées ⇒ constructible au-dessus sans toucher l’API scellée.
2csync — Once / lazy-init(aucun type Once/AirOnce) — briques : air_thread::atomic::AtomicU32 + futex ci-dessusAdditifOnce = AtomicU32 + futex ; fondations présentes.
3thread_local (#[thread_local], TLS/TCB)air_runtime::thread_control_block::ThreadControlBlock (errno, canari, DTV, tid ; :69) ; air_runtime::thread_local_storage::{ThreadLocalStorage, init_spawned_thread_tls} (:460/626) ; errno vrai #[thread_local] sur cible (thread_control_block.rs:289) — Variant I/II, ADR-049 D2/D3, prouvé on-targetAdditif — brique TLS/TCB fondatrice présente. Manque : destructeurs thread_local! (Drop fin de thread, ADR-049 D9) — champ DTV présent, pas de register_dtor. Mineur (panic=abort).
4time — Instant / SystemTimeair_base_core::time::{AirInstant, AirSystemTime, AirDuration} (:49/76/55) — CLOCK_MONOTONIC/CLOCK_REALTIME, UNIX_EPOCH, duration_since -> AirResult (recul d’horloge honnête)
5fs — File / metadata / dirair_filesystem::AirFileSystem = façade path-level : canonicalize/exists/write_atomic/read_to_bytes/copy/read_dir/glob (paths.rs, atomic.rs, search.rs) + AirTempDir + watcher inotify. Syscalls fichier bruts : couche 0 air_sys_syscall::fs::{openat2, read, write, pread/pwrite, lseek, statx, getdents64, mkdirat, unlinkat, renameat2, symlinkat, readlinkat, ftruncate, fsync} (fs.rs)Additifpas d’objet File couche 1 (open/read/write/seek/metadata). Le socle couche 0 est scellé et complet ; il manque le handle AirFile dans air-filesystem (brique existante) → ajout additif, aucune refonte. (Voir Gaps additifs — c’est le plus substantiel.)
6net — TCP / UDP / lookupair_socket::{AirTcpSocket, AirTcpListener, AirUdpSocket, AirUnixStream/Listener/Datagram} (connect/bind/listen/accept/read/write/send_to/recv_from/shutdown/as_fd) + AirNameResolver::{resolve, resolve_socket} (résolveur DNS maison, hosts/resolv.conf)
7process — Command / Child / exitair_process::{AirCommand (builder + spawn), AirProcess (wait_*/try_wait/signal/kill/pidfd/take_std*), AirExitStatus, AirStdio} + air_pipe + exit_process(code) -> ! (clone3/pidfd/exec internes)
8env — varsair_env::{get, vars, Vars} (octets, lecture seule ; set_environ au démarrage)Additiflecture ✅, mais set/unset (mutation setenv) absents. std::env::set_var en aura besoin → ajout additif à air-env.
15args (argv)air_runtime::args::{count, get, args, Args} (octets ; :27/46/66)
9random (seed HashMap / getrandom)Couche 0 air_sys_syscall::system::getrandom surfacé par air_crypto::AirRandom::fill(&mut [u8]) -> AirResult<()> (random.rs:60) et air_base_core::id (usage privé UUIDv7). CSPRNG kernel, jamais PRNG userspace.— (le PAL peut lier AirRandom::fill ou directement couche-0 getrandom pour éviter de tirer les deps RustCrypto)
10stdio — Stdin/out/errair_stdio::{stdin, stdout, stderr} -> StandardStream : read/write bruts (fd 0/1/2), is_terminal, lock() -> StreamGuard, reset_after_fork (atfork). no_std, verrou par flux modélisé loom
11path — séparateurs / composantsair_base_core::path::{AirPath, ComponentIterator} : séparateur / (POSIX), components/parent/join/file_name/extension/is_absolute/normalize_lexically/to_c_string (path.rs)
12alloc (GlobalAlloc)air_alloc::GlobalAllocator : unsafe impl core::alloc::GlobalAllocalloc/alloc_zeroed/dealloc/realloc (global.rs:75), const fn new, backing mmap réel (MmapBacking), OOM = null déterministe (jamais de hang). #[global_allocator] static câblé dans rt/air-rt.
13os_str (OsStr/OsString octets)Substrat présent : AirPath (octets possédés) + conventions &[u8]/Vec<u8> (env/args/AirDirEntry). Pas de paire newtype OsStr/OsString dédiée (AirPath = owned-only, emprunts en &[u8] brut)Additif — le PAL os_str Unix EST un wrapper de [u8]/Vec<u8> : substrat trivialement présent ; newtype AirOsStr/AirOsString = ajout additif si l’on veut une paire nommée.
14backtrace / unwindpanic = abort partout (ADR-045/ADR-049 D9) ⇒ pas d’unwind cross-thread ; sortie via air_process::exit_process / couche 0 exit_group(status) -> ! (process.rs:688)— (surface std::sys backtrace/unwind minimale par construction)

2. Points durs tranchés

2.1 — sync : Condvar & Once (grep)

Confirmé par grep (air-thread/src/, arbre entier) : aucun Condvar/AirCondvar, aucun Once/AirOnce. La surface sync exportée est exactement AirMutex/AirRwLock/AirSemaphore (+ guards) et air_channel (MPSC).

Statut : ❌ absents, mais ADDITIFS. Les deux se construisent au-dessus des primitives futex fondatrices déjà publiques (futex_wait_word/futex_wake_one/ reset_futex_word + AtomicU32). Les ajouter = nouveaux types dans air-thread (brique existante), sans nouvelle surface couche 0 ni refonte d’API scellée → descellement additif v1.x (ADR-051). Non bloquant.

2.2 — TLS (#[thread_local] / TCB) pour la face PAL

Statut : ◐ — brique fondatrice présente. air-runtime fournit le TCB (ThreadControlBlock : errno, canari %fs:0x28/tpidr_el0, DTV, tid) et le TLS ELF (ThreadLocalStorage, Variant I/II, init_spawned_thread_tls, install_main), avec un vrai errno #[thread_local] sur *-linux-air (ADR-049 D3), prouvé on-target (selftest rt/, 2 arches). C’est la brique que std::sys::thread_local réclame. Manque additif : l’exécution des destructeurs thread_local! en fin de thread (ADR-049 D9) — le champ DTV existe mais aucun register_dtor n’est câblé. Impact mineur (panic=abort, threads courts). Non bloquant.

2.3 — random : quelle brique ?

La seed HashMap / getrandom de std::sys se lie à air_crypto::AirRandom::fill (random.rs:60), lui-même sur la couche 0 air_sys_syscall::system::getrandom. Recommandation : pour le PAL, lier directement la couche-0 getrandom (comme le fait déjà air_base_core::id) plutôt que de tirer toute la fermeture RustCrypto d’air-crypto — cela évite aussi le point dur cpufeatureslibc d’aarch64 (§2.5). ✅ présent.

2.4 — path / os_str : AirPath couvre-t-il la face PAL ?

path : ✅. AirPath couvre exactement la logique std::sys::path Unix : séparateur /, itération de composants (ComponentIterator&[u8]), parent, join, is_absolute, file_name, extension, normalize_lexically, to_c_string (FFI syscall). os_str : ◐ additifAirPath est owned-only (pas de paire empruntée &OsStr), et il n’existe pas de newtype OsStr/OsString générique ; sur Unix, std::sys::os_str n’est qu’un wrapper de [u8]/Vec<u8>, substrat déjà présent partout. Newtype dédié = additif.

2.5 — fermeture on-target (rappel + caveat aarch64)

§2.A a prouvé la fermeture no_std icu pour x86_64/aarch64-unknown-linux-air (release, build-std=core,alloc, LSTM inclus). C’est une propriété de fermeture de lien, distincte de la complétude d’API objet de cet audit. Caveat (§2.E.2) : sur aarch64, la dép cpufeatures d’air-crypto lie libc (getauxval) sans profil env=air → chemin air_auxval(AT_HWCAP) libc-free à fournir. C’est une limitation de fermeture (chantier dédié), pas un manque d’API PAL : le PAL n’a pas besoin d’air-crypto pour son random (il lie couche-0 getrandom).


3. Gaps BLOQUANTS (fondateur absent)

Aucun.

Toutes les primitives std::sys disposent de leur brique fondatrice en couche 1 (ou de son socle couche-0 scellé + point d’ancrage couche-1) :

  • l’allocateur global (GlobalAlloc complet, backing mmap réel) — présent ;
  • le spawn natif avec bootstrap TCB/TLS (la partie unsound-si-fausse, qui fige la disposition TLS qu’on scelle) — présent et prouvé on-target ;
  • les verrous Mutex/RwLock sur futex maison + les primitives futex exposées — présents ;
  • time, net, process, stdio, path, args, random, exit — présents ;
  • le socle fs (syscalls fichier couche 0) — scellé et complet.

Rien de ce qui reste ne peut casser une API scellée : ce sont des ajouts au-dessus.


4. Gaps ADDITIFS post-sceau (comblables en v1.x, sans refonte)

Classés par importance décroissante. Aucun ne modifie une API scellée ; chacun s’ajoute à une brique existante (ou au toit PAL) au-dessus de fondations figées.

  1. fs — handle AirFile couche 1 (le plus substantiel). air-filesystem n’expose pas de type fichier (open/read/write/seek/metadata) ; seulement des ops path-level. Le socle couche-0 (openat2/read/write/lseek/statx/ getdents64…) est scellé et complet. → ajouter un objet AirFile (RAII OwnedFd) + metadata/DirEntry dans air-filesystem. Additif (brique existante, socle figé, cohérent avec la façon dont air-socket/air-process exposent déjà leurs handles I/O).
  2. thread — spawn ergonomique boxé (FnOnce()->T + résultat au join) sur la cible. Voir §5 (recommandation dédiée). Additif.
  3. syncCondvar et Once — au-dessus des primitives futex publiques (§2.1). Additif.
  4. env — mutation (set/unset, std::env::set_var). air-env est lecture-seule aujourd’hui. Additif.
  5. os_str — newtype AirOsStr/AirOsString (paire owned/emprunté octets). Substrat [u8]/Vec<u8> déjà présent. Additif.
  6. thread_local — destructeurs thread_local! (ADR-049 D9) — DTV présent, register_dtor à câbler. Additif, mineur (panic=abort).
  7. thread — divers cible : yield_now (passthrough couche-0 sched_yield à surfacer en couche 1), nommage de thread sur cible (prctl(PR_SET_NAME)). Additifs, triviaux.

5. Recommandation argumentée — spawn ergonomique natif (§2.B.2)

Question ouverte §2.B.2. L’API std::thread::spawn a-t-elle sa brique ergonomique sur la cible ? Le spawn natif bas-niveau existe (air_runtime::thread::spawn(entry: extern "C" fn(usize), arg: usize) + air_thread::runtime_primitives::create_thread), mais l’ergonomique boxé (FnOnce()->T via air-alloc + trampoline extern "C" + résultat récupéré au join) n’est pas câblé sur *-linux-air. Sur l’hôte, AirThreadBuilder::spawn existe mais enrobe std::thread et est gardé #[cfg(not(target_env = "air"))] — donc absent de la cible, qui est précisément celle du PAL.

Verdict : ADDITIF (non bloquant pour le sceau)

Argumentaire :

  1. La partie FONDATRICE est déjà là. Le difficile — et le dangereux-si-faux — du spawn Air-natif, c’est le bootstrap TCB/TLS : allouer pile + image TLS, clone3 + CLONE_SETTLS, faire résoudre errno/#[thread_local] dans le bloc du thread, joindre par CHILD_CLEARTID/futex. C’est exactement ce qui fige la disposition TLS que le sceau grave — et c’est présent, testé on-target (air_runtime::thread::spawn + create_thread, selftest rt/). Si cette pièce manquait, ce serait bloquant (impossible à ajouter plus tard sans toucher la disposition scellée). Elle ne manque pas.

  2. Ce qui manque est de la pure composition, au-dessus de deux fondations déjà présentes et à sceller : (a) le spawn natif ci-dessus, (b) l’allocateur global air_alloc::GlobalAllocator (✅). L’ergonomique = boxer la closure (Box<dyn FnOnce()> via air-alloc), un trampoline extern "C" fn(usize) qui reconstitue le Box et l’appelle, et une case tas où déposer T, lue au join. Zéro nouvelle surface couche 0, zéro modification d’API scellée. Avec panic=abort (ADR-049 D9), la « capture de panique au join » est sans objet (un thread qui panique abort le process) : le join n’a qu’à rendre T.

  3. Le modèle std lui-même sépare ces deux étages : std::sys fournit le Thread::new(stack, main: Box<dyn FnOnce()>) bas-niveau, et std::thread pose au-dessus le plombage du résultat/handle. Notre découpe est donc alignée sur l’amont : couche 1 = le natif ; le boxage/résultat s’ajoute proprement au-dessus.

Où le poser (décision de couche)

  • PAS dans air-thread. Cycle : l’ergonomique a besoin du spawn natif, qui vit dans air-runtime, lequel dépend déjà d’air-thread (runtime_primitives::create_thread) → air-thread → air-runtime bouclerait. (C’est la contrainte notée en §2.B.2 de la roadmap.)
  • Recommandé : dans air-runtime (nouveau point d’entrée, p. ex. air_runtime::thread::spawn_boxed<F, T>(f: F) -> Result<JoinHandle<T>, Errno>), qui : (i) possède déjà le spawn natif et la JoinHandle (cohésion) ; (ii) peut dépendre d’air-alloc sans cycle — la dépendance air-runtime → air-alloc existe déjà (air_runtime::fork appelle air_alloc::reset_after_fork). C’est l’emplacement que la roadmap §2.B.2 anticipe (« le spawn Air-natif viendra d’air-runtime + allocateur réel »).
  • Variante acceptable : laisser le plombage T-au-join dans le toit PAL (std::sys::thread de *-linux-air) et n’exiger de couche 1 que l’acceptation d’une closure boxée (Box<dyn FnOnce()>) — calqué sur std::sys::pal::unix. Dans les deux cas : additif, aucune API scellée touchée.

Conséquence pour le sceau : sceller maintenant le spawn natif + le GlobalAlloc ; l’ergonomique se pose après en couche additive v1.x. Le sceau n’a pas à attendre spawn_boxed.


6. VERDICT de sceau-readiness face-PAL

OUI — la couche 1 est brique-complète pour porter le PAL std::sys.

Justification. Toutes les primitives std::sys fondatrices disposent de leur brique couche 1, réelle et (pour le code target-only) prouvée on-target : allocateur global (GlobalAlloc complet), spawn natif avec bootstrap TCB/TLS (la pièce qui fige la disposition scellée), verrous Mutex/RwLock sur futex maison + primitives futex exposées, temps monotone/mur + sleep, réseau (TCP/UDP/Unix + DNS), process (Command/Child/exit), stdio brut (fd 0/1/2 + isatty + verrou + atfork), chemins octets, argv/env (lecture), random (CSPRNG kernel), et exit/panic=abort pour la face backtrace/unwind minimale.

Aucun gap BLOQUANT (§3). Les manques recensés (§4) — handle fs::File couche 1, spawn ergonomique boxé (§5), Condvar/Once, mutation env, newtype os_str, destructeurs TLS, yield_now/nommage cible — sont tous ADDITIFS : ils se construisent au-dessus de fondations figées (socle couche-0 scellé + briques couche-1 fondatrices), sans refonte d’aucune API que le sceau grave. Ils relèvent du comblement additif §5.3 et/ou du descellement additif v1.x (modèle ADR-051), pas d’un pré-requis au sceau.

Réserve de fermeture (non-API). Sur aarch64, la fermeture de lien reste imparfaite tant que le chemin air_auxval(AT_HWCAP) libc-free n’est pas fourni (§2.5, §2.E.2) — mais c’est une propriété de lien, pas de complétude d’API PAL, et le random du PAL n’y est pas exposé (lien couche-0 getrandom direct).


7. Synthèse (mémo)

Face PALVerdict
Primitives fondatricestoutes présentes (alloc, spawn natif+TCB/TLS, Mutex/RwLock+futex, time, net, process, stdio, path, args, env-lecture, random, exit)
Gaps bloquants0
Gaps additifsfs::File (handle couche 1) · spawn ergonomique boxé · Condvar/Once · env mutation · os_str newtype · dtors TLS · yield/nommage cible
§2.B.2 (spawn ergo)ADDITIF, à poser dans air-runtime (spawn_boxed, dépend d’air-alloc — pas de cycle) ; le natif+TCB/TLS, déjà scellable, suffit au sceau
Sceau-readiness face-PALOUI