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

Spec couche 0 — Famille time

Spécification technique — Version 1.0

Vue d’ensemble de la famille

Le module air-sys-syscall::time expose les primitives de gestion du temps : horloges système, timers FD, sleeps précis. C’est une petite famille (~8 syscalls) mais essentielle pour tous les patterns qui ont besoin de mesurer ou d’attendre des durées.

Caractéristiques transverses de la famille.

  1. Multiples horloges Linux. Le kernel expose plusieurs horloges aux sémantiques distinctes : CLOCK_REALTIME (heure murale, ajustable), CLOCK_MONOTONIC (monotone depuis le boot), CLOCK_BOOTTIME (inclut le suspend), et d’autres. Le wrapper Air les expose comme un enum typé pour empêcher la confusion entre horloges incompatibles.

  2. Timerfd intégré io_uring. Comme signalfd, timerfd est un FD lisible quand le timer expire. Cela s’intègre naturellement avec io_uring.

  3. Précision nanoseconde. Les structures Timespec du kernel expriment le temps avec une résolution nanoseconde.

  4. Gestion EINTR. clock_nanosleep et les lectures bloquantes sur timerfd peuvent retourner EINTR. Conformément à la convention 2 de l’ADR-021, remonté à l’appelant.

  5. Représentations duales. Air expose à la fois Duration (standard Rust) pour les durées et Instant (custom Air) pour les points dans le temps.

Sous-section 1 : Horloges et instants

Type Clock

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum Clock {
    Realtime = 0,
    Monotonic = 1,
    ProcessCpuTime = 2,
    ThreadCpuTime = 3,
    MonotonicRaw = 4,
    RealtimeCoarse = 5,
    MonotonicCoarse = 6,
    Boottime = 7,
    RealtimeAlarm = 8,
    BoottimeAlarm = 9,
    Tai = 11,
}

impl Clock {
    pub const fn as_raw(self) -> i32;
    pub fn try_from_raw(value: i32) -> Option<Self>;
    pub fn name(self) -> &'static str;
}
}

Sémantiques des horloges.

  • Realtime : heure murale UTC. Ajustable par NTP. Discontinue.
  • Monotonic : compteur depuis un point arbitraire. Strictement croissant. Pour mesurer des durées.
  • ProcessCpuTime : temps CPU consommé par le processus.
  • ThreadCpuTime : temps CPU consommé par le thread.
  • MonotonicRaw : comme Monotonic mais non affecté par NTP slewing.
  • RealtimeCoarse, MonotonicCoarse : versions à résolution réduite, plus rapides.
  • Boottime : comme Monotonic mais inclut le suspend.
  • RealtimeAlarm, BoottimeAlarm : peuvent réveiller le système depuis suspend.
  • Tai : International Atomic Time.

Recommandations d’usage.

  • Mesurer une durée : Monotonic.
  • Timer ou timeout : Monotonic ou Boottime (si suspend-aware).
  • Timestamp utilisateur : Realtime.
  • Réveil de la machine : BoottimeAlarm (avec privilèges).

clock_gettime

#![allow(unused)]
fn main() {
pub fn clock_gettime(clock: Clock) -> Result<Instant, Errno>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
    clock: Clock,
    seconds: i64,
    nanoseconds: u32,
}

impl Instant {
    pub fn clock(&self) -> Clock;
    pub fn seconds(&self) -> i64;
    pub fn nanoseconds(&self) -> u32;
    pub fn as_duration_since_epoch(&self) -> Duration;
    
    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration>;
    pub fn saturating_duration_since(&self, earlier: Instant) -> Duration;
}
}

Comportement.

Lit le temps courant pour l’horloge spécifiée. Retourne un Instant qui inclut l’horloge utilisée.

Pas d’Instant arithmétique cross-clock.

Le type empêche par construction les opérations entre Instant d’horloges différentes.

Performance. ~30-100 ns via vDSO pour les horloges principales.

clock_settime et clock_getres

#![allow(unused)]
fn main() {
pub fn clock_settime(clock: Clock, instant: Instant) -> Result<(), Errno>;
pub fn clock_getres(clock: Clock) -> Result<Duration, Errno>;
}

clock_settime demande CAP_SYS_TIME. Usage rare.

clock_getres retourne la résolution de l’horloge.

Sous-section 2 : Sleep

clock_nanosleep

#![allow(unused)]
fn main() {
pub fn clock_nanosleep(
    clock: Clock,
    deadline: SleepDeadline,
) -> Result<(), SleepError>;

pub enum SleepDeadline {
    Relative(Duration),
    AbsoluteInstant(Instant),
}

pub enum SleepError {
    Interrupted { remaining: Duration },
    Other(Errno),
}
}

Comportement.

Bloque le thread jusqu’à l’instant spécifié (mode absolu) ou pendant la durée spécifiée (mode relatif). Le mode absolu est généralement préféré pour éviter les dérives.

Gestion EINTR particulière.

En cas d’EINTR avec mode relatif, le kernel retourne le temps restant à dormir. Le wrapper Air le récupère et le passe via SleepError::Interrupted { remaining }.

Sous-section 3 : Timerfd

timerfd_create

#![allow(unused)]
fn main() {
pub fn timerfd_create(
    clock: Clock,
    flags: TimerFdFlags,
) -> Result<TimerFd, Errno>;

pub struct TimerFd { /* possède OwnedFd interne */ }

impl TimerFd {
    pub fn as_fd(&self) -> BorrowedFd<'_>;
    pub fn into_fd(self) -> OwnedFd;
    
    pub fn arm(&self, spec: &TimerFdSpecification, flags: TimerSetFlags) -> Result<TimerFdSpecification, Errno>;
    pub fn disarm(&self) -> Result<TimerFdSpecification, Errno>;
    pub fn current(&self) -> Result<TimerFdSpecification, Errno>;
    
    pub fn read(&self) -> Result<u64, Errno>;
}

bitflags! {
    pub struct TimerFdFlags: i32 {
        const NONBLOCK = 0x800;
        const CLOEXEC = 0x80000;
    }
}

bitflags! {
    pub struct TimerSetFlags: i32 {
        const ABSTIME = 1;
        const CANCEL_ON_SET = 2;
    }
}

#[derive(Debug, Clone, Copy)]
pub struct TimerFdSpecification {
    pub initial: Duration,
    pub interval: Duration,
}
}

Syscall sous-jacent. timerfd_create (x86_64 n°283, ARM64 n°85).

Comportement.

Crée un FD timerfd. L’application l’arme via arm() avec une spécification.

À chaque expiration, le FD devient lisible. read() retourne le nombre d’expirations survenues.

Modes de spec.

  • One-shot : initial = duration, interval = ZERO.
  • Periodic : initial = duration, interval = period.
  • Disarmed : initial = ZERO, interval = ZERO.

ABSTIME et CANCEL_ON_SET.

ABSTIME : initial est un instant absolu.

CANCEL_ON_SET : si l’horloge Realtime est ajustée, le timer est annulé.

Pattern d’usage.

#![allow(unused)]
fn main() {
let tfd = timerfd_create(Clock::Monotonic, TimerFdFlags::empty())?;
let spec = TimerFdSpecification {
    initial: Duration::from_secs(5),
    interval: Duration::ZERO,
};
tfd.arm(&spec, TimerSetFlags::empty())?;

// Attendre l'expiration
let expirations = tfd.read()?;

// Ou via io_uring
ring.submit_read(tfd.as_fd(), buf, 0)?;
}

Récapitulatif de la famille time

Fonctions exposées :

CatégorieFonctions principales
Horlogesclock_gettime, clock_settime, clock_getres
Sleepclock_nanosleep
Timerfdtimerfd_create, TimerFd::arm, TimerFd::disarm, TimerFd::current, TimerFd::read

Total : ~8 fonctions publiques principales.

Syscalls non-wrappés (listés dans UNSUPPORTED.md) :

  • getitimer, setitimer (legacy)
  • times (couvert par clock_gettime CpuTime)
  • time, gettimeofday (legacy)
  • adjtimex, clock_adjtime (phase 0 reportée)
  • nanosleep (remplacé par clock_nanosleep)

Types ajoutés à air-sys-types

  • Clock
  • Instant
  • Timespec (interne)
  • TimerFd, TimerFdSpecification, TimerFdFlags, TimerSetFlags
  • SleepDeadline, SleepError

Soit ~9 types supplémentaires.

Décisions de fond émergées dans la famille time

1. Instant typé par horloge.

Empêche par construction les bugs de mélange d’horloges.

2. SleepError distinct de Errno.

Type d’erreur dédié pour exposer l’information “remaining” pour le cas EINTR.

3. Pas d’API std::time::Instant directe.

Le type Instant Air est distinct de std::time::Instant. Conversion fournie mais pas implicite.

4. TimerFd::read() retourne le nombre d’expirations.

Important pour les patterns temps réel.


Licence du document : MPL 2.0 Statut : Spécification technique du module air-sys-syscall::time (couche 0).