starnix_core/task/
timeline.rs

1// Copyright 2024 The Fuchsia Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::time::utc;
6use fuchsia_runtime::{UtcDuration, UtcInstant};
7use starnix_types::time::{itimerspec_from_deadline_interval, time_from_timespec};
8use starnix_uapi::errors::Errno;
9use starnix_uapi::{itimerspec, timespec};
10use std::ops;
11
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub enum Timeline {
14    RealTime,
15    Monotonic,
16    BootInstant,
17}
18
19impl Timeline {
20    /// Returns the current time on this timeline.
21    pub fn now(&self) -> TargetTime {
22        match self {
23            Self::RealTime => TargetTime::RealTime(utc::utc_now()),
24            Self::Monotonic => TargetTime::Monotonic(zx::MonotonicInstant::get()),
25            Self::BootInstant => TargetTime::BootInstant(zx::BootInstant::get()),
26        }
27    }
28
29    pub fn target_from_timespec(&self, spec: timespec) -> Result<TargetTime, Errno> {
30        Ok(match self {
31            Timeline::Monotonic => TargetTime::Monotonic(time_from_timespec(spec)?),
32            Timeline::RealTime => TargetTime::RealTime(time_from_timespec(spec)?),
33            Timeline::BootInstant => TargetTime::BootInstant(time_from_timespec(spec)?),
34        })
35    }
36
37    pub fn zero_time(&self) -> TargetTime {
38        match self {
39            Timeline::Monotonic => TargetTime::Monotonic(zx::Instant::ZERO),
40            Timeline::RealTime => TargetTime::RealTime(zx::Instant::ZERO),
41            Timeline::BootInstant => TargetTime::BootInstant(zx::Instant::ZERO),
42        }
43    }
44
45    /// Return true if this timeline represents a realtime clock timeline.
46    pub fn is_realtime(&self) -> bool {
47        match self {
48            Timeline::RealTime => true,
49            _ => false,
50        }
51    }
52}
53
54#[derive(Debug)]
55pub enum TimerWakeup {
56    /// A regular timer that does not wake the system if it is suspended.
57    Regular,
58    /// An alarm timer that will wake the system if it is suspended.
59    Alarm,
60}
61
62#[derive(Clone, Copy, Debug, PartialEq)]
63pub enum TargetTime {
64    Monotonic(zx::MonotonicInstant),
65    RealTime(UtcInstant),
66    BootInstant(zx::BootInstant),
67}
68
69impl std::fmt::Display for TargetTime {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        match self {
72            TargetTime::Monotonic(t) => write!(f, "{} [MONO]", time_pretty::format_timer(*t)),
73            TargetTime::BootInstant(t) => write!(f, "{} [BOOT]", time_pretty::format_timer(*t)),
74            TargetTime::RealTime(t) => write!(f, "{} [UTC]", time_pretty::format_timer(*t)),
75        }
76    }
77}
78
79impl From<zx::BootInstant> for TargetTime {
80    fn from(value: zx::BootInstant) -> Self {
81        Self::BootInstant(value)
82    }
83}
84
85impl From<UtcInstant> for TargetTime {
86    fn from(value: UtcInstant) -> Self {
87        Self::RealTime(value)
88    }
89}
90
91impl TargetTime {
92    pub fn is_zero(&self) -> bool {
93        0 == match self {
94            TargetTime::Monotonic(t) => t.into_nanos(),
95            TargetTime::RealTime(t) => t.into_nanos(),
96            TargetTime::BootInstant(t) => t.into_nanos(),
97        }
98    }
99
100    pub fn itimerspec(&self, interval: zx::MonotonicDuration) -> itimerspec {
101        match self {
102            TargetTime::Monotonic(t) => itimerspec_from_deadline_interval(*t, interval),
103            TargetTime::BootInstant(t) => itimerspec_from_deadline_interval(*t, interval),
104            TargetTime::RealTime(t) => itimerspec_from_deadline_interval(*t, interval),
105        }
106    }
107
108    pub fn estimate_boot(&self) -> Option<zx::BootInstant> {
109        match self {
110            TargetTime::BootInstant(t) => Some(*t),
111            TargetTime::RealTime(t) => {
112                let (boot_instant, _) = utc::estimate_boot_deadline_from_utc(*t);
113                Some(boot_instant)
114            }
115            // It's not possible to estimate how long suspensions will be.
116            TargetTime::Monotonic(_) => None,
117        }
118    }
119
120    /// Attempts to resolve a deadline based on UTC.
121    ///
122    /// If the UTC clock is started, the UTC deadline is deemed accurate and retained. If not, then
123    /// we emulate the deadline by mapping the fake UTC timestamp to the boot timeline. Such
124    /// alarm will *not* move with the UTC timeline changes.
125    pub fn into_resolved_utc_deadline(self) -> TargetTime {
126        match self {
127            orig @ TargetTime::RealTime(t) => {
128                let (boot_instant, started) = utc::estimate_boot_deadline_from_utc(t);
129                if started {
130                    orig
131                } else {
132                    // If the Fuchsia UTC clock is not started yet, emulate with
133                    // the boot timeline. This allows placing a timer on the UTC
134                    // timeline even if Fuchsia UTC clock has not been started.
135                    TargetTime::BootInstant(boot_instant)
136                }
137            }
138            other @ _ => other,
139        }
140    }
141
142    /// Find the difference between this time and `rhs`. Returns `None` if the timelines don't
143    /// match.
144    pub fn delta(&self, rhs: &Self) -> Option<GenericDuration> {
145        match (*self, *rhs) {
146            (TargetTime::Monotonic(lhs), TargetTime::Monotonic(rhs)) => {
147                Some(GenericDuration::from(lhs - rhs))
148            }
149            (TargetTime::BootInstant(lhs), TargetTime::BootInstant(rhs)) => {
150                Some(GenericDuration::from(lhs - rhs))
151            }
152            (TargetTime::RealTime(lhs), TargetTime::RealTime(rhs)) => {
153                Some(GenericDuration::from(lhs - rhs))
154            }
155            _ => None,
156        }
157    }
158}
159
160impl std::ops::Add<GenericDuration> for TargetTime {
161    type Output = Self;
162    fn add(self, rhs: GenericDuration) -> Self {
163        match self {
164            Self::RealTime(t) => Self::RealTime(t + rhs.into_utc()),
165            Self::Monotonic(t) => Self::Monotonic(t + rhs.into_mono()),
166            Self::BootInstant(t) => Self::BootInstant(t + rhs.into_boot()),
167        }
168    }
169}
170
171impl std::ops::Sub<GenericDuration> for TargetTime {
172    type Output = Self;
173    fn sub(self, rhs: GenericDuration) -> Self::Output {
174        match self {
175            TargetTime::Monotonic(t) => Self::Monotonic(t - rhs.into_mono()),
176            TargetTime::RealTime(t) => Self::RealTime(t - rhs.into_utc()),
177            TargetTime::BootInstant(t) => Self::BootInstant(t - rhs.into_boot()),
178        }
179    }
180}
181
182impl std::cmp::PartialOrd for TargetTime {
183    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
184        match (self, other) {
185            (Self::Monotonic(lhs), Self::Monotonic(rhs)) => Some(lhs.cmp(rhs)),
186            (Self::RealTime(lhs), Self::RealTime(rhs)) => Some(lhs.cmp(rhs)),
187            (Self::BootInstant(lhs), Self::BootInstant(rhs)) => Some(lhs.cmp(rhs)),
188            _ => None,
189        }
190    }
191}
192
193/// This type is a convenience to use when the Timeline isn't clear in Starnix. It allows storing a
194/// generic nanosecond duration which can be used to operate on Instants or Durations from any
195/// Timeline.
196#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
197pub struct GenericDuration(zx::SyntheticDuration);
198
199impl GenericDuration {
200    pub fn from_nanos(nanos: zx::sys::zx_time_t) -> Self {
201        Self(zx::Duration::from_nanos(nanos))
202    }
203
204    pub fn into_mono(self) -> zx::MonotonicDuration {
205        zx::MonotonicDuration::from_nanos(self.0.into_nanos())
206    }
207
208    // TODO(https://fxbug.dev/328306129) handle boot and monotonic time properly and remove this
209    // allow.
210    #[allow(dead_code)]
211    fn into_boot(self) -> zx::BootDuration {
212        zx::BootDuration::from_nanos(self.0.into_nanos())
213    }
214
215    fn into_utc(self) -> UtcDuration {
216        UtcDuration::from_nanos(self.0.into_nanos())
217    }
218}
219
220impl From<zx::MonotonicDuration> for GenericDuration {
221    fn from(other: zx::MonotonicDuration) -> Self {
222        Self(zx::SyntheticDuration::from_nanos(other.into_nanos()))
223    }
224}
225
226impl From<zx::BootDuration> for GenericDuration {
227    fn from(other: zx::BootDuration) -> Self {
228        Self(zx::SyntheticDuration::from_nanos(other.into_nanos()))
229    }
230}
231
232impl From<zx::SyntheticDuration> for GenericDuration {
233    fn from(other: zx::SyntheticDuration) -> Self {
234        Self(other)
235    }
236}
237
238impl From<UtcDuration> for GenericDuration {
239    fn from(other: UtcDuration) -> Self {
240        Self(zx::SyntheticDuration::from_nanos(other.into_nanos()))
241    }
242}
243
244impl ops::Deref for GenericDuration {
245    type Target = zx::SyntheticDuration;
246
247    #[inline]
248    fn deref(&self) -> &Self::Target {
249        &self.0
250    }
251}