Skip to main content

air_sys_types/
time.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 fondamentaux de la famille `time`.
6//!
7//! Couvre les horloges, instants, délais de sommeil et drapeaux liés à
8//! `timerfd` (cf. `docs/specs/layer-0/family-time.md`).
9
10use core::fmt;
11use core::time::Duration;
12
13use bitflags::bitflags;
14
15const NANOS_PER_SECOND: u32 = 1_000_000_000;
16
17/// Horloge Linux (`clockid_t`).
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[repr(i32)]
20pub enum Clock {
21    /// Heure murale UTC, ajustable.
22    Realtime = 0,
23    /// Horloge monotone depuis le boot, hors suspend.
24    Monotonic = 1,
25    /// Temps CPU consommé par le processus.
26    ProcessCpuTime = 2,
27    /// Temps CPU consommé par le thread.
28    ThreadCpuTime = 3,
29    /// Horloge monotone brute, hors NTP slewing.
30    MonotonicRaw = 4,
31    /// Variante coarse de `Realtime`.
32    RealtimeCoarse = 5,
33    /// Variante coarse de `Monotonic`.
34    MonotonicCoarse = 6,
35    /// Horloge monotone incluant le suspend.
36    Boottime = 7,
37    /// Réveil basé sur `Realtime`.
38    RealtimeAlarm = 8,
39    /// Réveil basé sur `Boottime`.
40    BoottimeAlarm = 9,
41    /// Temps atomique international.
42    Tai = 11,
43}
44
45impl Clock {
46    /// Représentation ABI brute.
47    #[must_use]
48    #[inline]
49    pub const fn as_raw(self) -> i32 {
50        self as i32
51    }
52
53    /// Tente de reconstruire une horloge depuis une valeur brute.
54    #[must_use]
55    pub const fn try_from_raw(value: i32) -> Option<Self> {
56        match value {
57            0 => Some(Self::Realtime),
58            1 => Some(Self::Monotonic),
59            2 => Some(Self::ProcessCpuTime),
60            3 => Some(Self::ThreadCpuTime),
61            4 => Some(Self::MonotonicRaw),
62            5 => Some(Self::RealtimeCoarse),
63            6 => Some(Self::MonotonicCoarse),
64            7 => Some(Self::Boottime),
65            8 => Some(Self::RealtimeAlarm),
66            9 => Some(Self::BoottimeAlarm),
67            11 => Some(Self::Tai),
68            _ => None,
69        }
70    }
71
72    /// Nom stable, utile pour le debug et les messages d'erreur.
73    #[must_use]
74    pub const fn name(self) -> &'static str {
75        match self {
76            Self::Realtime => "CLOCK_REALTIME",
77            Self::Monotonic => "CLOCK_MONOTONIC",
78            Self::ProcessCpuTime => "CLOCK_PROCESS_CPUTIME_ID",
79            Self::ThreadCpuTime => "CLOCK_THREAD_CPUTIME_ID",
80            Self::MonotonicRaw => "CLOCK_MONOTONIC_RAW",
81            Self::RealtimeCoarse => "CLOCK_REALTIME_COARSE",
82            Self::MonotonicCoarse => "CLOCK_MONOTONIC_COARSE",
83            Self::Boottime => "CLOCK_BOOTTIME",
84            Self::RealtimeAlarm => "CLOCK_REALTIME_ALARM",
85            Self::BoottimeAlarm => "CLOCK_BOOTTIME_ALARM",
86            Self::Tai => "CLOCK_TAI",
87        }
88    }
89}
90
91/// Point dans le temps associé à une horloge Linux spécifique.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct Instant {
94    clock: Clock,
95    seconds: i64,
96    nanoseconds: u32,
97}
98
99impl Instant {
100    /// Construit un `Instant` après validation des champs temporels.
101    #[must_use]
102    pub const fn try_new(clock: Clock, seconds: i64, nanoseconds: u32) -> Option<Self> {
103        if nanoseconds >= NANOS_PER_SECOND {
104            return None;
105        }
106        Some(Self {
107            clock,
108            seconds,
109            nanoseconds,
110        })
111    }
112
113    /// Horloge portée par cet instant.
114    #[must_use]
115    #[inline]
116    pub const fn clock(self) -> Clock {
117        self.clock
118    }
119
120    /// Composante secondes.
121    #[must_use]
122    #[inline]
123    pub const fn seconds(self) -> i64 {
124        self.seconds
125    }
126
127    /// Composante nanosecondes, toujours `< 1_000_000_000`.
128    #[must_use]
129    #[inline]
130    pub const fn nanoseconds(self) -> u32 {
131        self.nanoseconds
132    }
133
134    /// Conversion en durée depuis l'époque de l'horloge.
135    ///
136    /// Si la composante secondes est négative, retourne `Duration::ZERO`.
137    #[must_use]
138    pub fn as_duration_since_epoch(self) -> Duration {
139        if self.seconds < 0 {
140            return Duration::ZERO;
141        }
142        let secs = u64::try_from(self.seconds).expect("non-negative seconds checked above");
143        Duration::new(secs, self.nanoseconds)
144    }
145
146    /// Différence positive entre deux instants de même horloge.
147    #[must_use]
148    pub fn checked_duration_since(self, earlier: Instant) -> Option<Duration> {
149        if self.clock != earlier.clock {
150            return None;
151        }
152        if (self.seconds, self.nanoseconds) < (earlier.seconds, earlier.nanoseconds) {
153            return None;
154        }
155        let nanos_per_second = i128::from(NANOS_PER_SECOND);
156        let self_nanos = i128::from(self.seconds)
157            .checked_mul(nanos_per_second)?
158            .checked_add(i128::from(self.nanoseconds))?;
159        let earlier_nanos = i128::from(earlier.seconds)
160            .checked_mul(nanos_per_second)?
161            .checked_add(i128::from(earlier.nanoseconds))?;
162        let diff = self_nanos.checked_sub(earlier_nanos)?;
163        let secs_i128 = diff.checked_div(nanos_per_second)?;
164        let nanos_i128 = diff.checked_rem(nanos_per_second)?;
165        let secs_u64 = u64::try_from(secs_i128).ok()?;
166        let nanos = u32::try_from(nanos_i128).ok()?;
167        Some(Duration::new(secs_u64, nanos))
168    }
169
170    /// Variante saturante de [`Self::checked_duration_since`].
171    #[must_use]
172    pub fn saturating_duration_since(self, earlier: Instant) -> Duration {
173        self.checked_duration_since(earlier)
174            .unwrap_or(Duration::ZERO)
175    }
176}
177
178/// Échéance passée à `clock_nanosleep`.
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
180pub enum SleepDeadline {
181    /// Délai relatif depuis maintenant.
182    Relative(Duration),
183    /// Instant absolu sur l'horloge ciblée.
184    AbsoluteInstant(Instant),
185}
186
187/// Erreur spécialisée de `clock_nanosleep`.
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum SleepError {
190    /// Le sommeil relatif a été interrompu ; le kernel a fourni le reliquat.
191    Interrupted {
192        /// Durée restante fournie par le kernel au moment de l'interruption.
193        remaining: Duration,
194    },
195    /// Toute autre erreur renvoyée telle quelle.
196    Other(crate::Errno),
197}
198
199impl fmt::Display for SleepError {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        match self {
202            Self::Interrupted { remaining } => {
203                write!(f, "sleep interrupted with {:?} remaining", remaining)
204            }
205            Self::Other(errno) => errno.fmt(f),
206        }
207    }
208}
209
210impl core::error::Error for SleepError {}
211
212bitflags! {
213    /// Drapeaux de `timerfd_create`.
214    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
215    pub struct TimerFdFlags: i32 {
216        /// FD non bloquant.
217        const NONBLOCK = 0x800;
218        /// Fermeture automatique sur `execve`.
219        const CLOEXEC = 0x80000;
220    }
221}
222
223bitflags! {
224    /// Drapeaux de `timerfd_settime`.
225    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
226    pub struct TimerSetFlags: i32 {
227        /// `initial` est interprété comme une échéance absolue.
228        const ABSTIME = 1;
229        /// Annulation si `CLOCK_REALTIME` est ajustée.
230        const CANCEL_ON_SET = 2;
231    }
232}
233
234/// Spécification de timerfd : première expiration puis intervalle périodique.
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
236pub struct TimerFdSpecification {
237    /// Première expiration.
238    pub initial: Duration,
239    /// Intervalle périodique ; `Duration::ZERO` = one-shot.
240    pub interval: Duration,
241}
242
243#[cfg(test)]
244mod tests;