starnix_types/
time.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use linux_uapi::itimerval;
6use starnix_uapi::errors::{Errno, error};
7use starnix_uapi::{itimerspec, timespec, timeval};
8use static_assertions::const_assert_eq;
9
10const MICROS_PER_SECOND: i64 = 1000 * 1000;
11pub const NANOS_PER_SECOND: i64 = 1000 * 1000 * 1000;
12
13// Frequence of the "scheduler clock", which is used to report time values in some APIs, e.g. in
14// `/proc` and `times()`. The same clock may be referred to as "USER_HZ" or "clock ticks".
15// Passed to Linux processes by the loader as `AT_CLKTCK`, which they can get it from libc using
16// `sysconf(_SC_CLK_TCK)`. Linux usually uses 100Hz.
17pub const SCHEDULER_CLOCK_HZ: i64 = 100;
18
19const_assert_eq!(NANOS_PER_SECOND % SCHEDULER_CLOCK_HZ, 0);
20const NANOS_PER_SCHEDULER_TICK: i64 = NANOS_PER_SECOND / SCHEDULER_CLOCK_HZ;
21
22pub fn timeval_from_time<T: zx::Timeline>(time: zx::Instant<T>) -> timeval {
23    let nanos = time.into_nanos();
24    timeval { tv_sec: nanos / NANOS_PER_SECOND, tv_usec: (nanos % NANOS_PER_SECOND) / 1000 }
25}
26
27pub fn timeval_from_duration<T: zx::Timeline>(duration: zx::Duration<T>) -> timeval {
28    let nanos = duration.into_nanos();
29    timeval { tv_sec: nanos / NANOS_PER_SECOND, tv_usec: (nanos % NANOS_PER_SECOND) / 1000 }
30}
31
32pub fn timespec_from_time<T: zx::Timeline>(time: zx::Instant<T>) -> timespec {
33    let nanos = time.into_nanos();
34    timespec { tv_sec: nanos / NANOS_PER_SECOND, tv_nsec: nanos % NANOS_PER_SECOND }
35}
36
37pub fn timespec_from_duration<T: zx::Timeline>(duration: zx::Duration<T>) -> timespec {
38    let nanos = duration.into_nanos();
39    timespec { tv_sec: nanos / NANOS_PER_SECOND, tv_nsec: nanos % NANOS_PER_SECOND }
40}
41
42pub fn duration_from_timespec<T: zx::Timeline>(ts: timespec) -> Result<zx::Duration<T>, Errno> {
43    if ts.tv_nsec >= NANOS_PER_SECOND {
44        return error!(EINVAL);
45    }
46    if ts.tv_sec < 0 || ts.tv_nsec < 0 {
47        return error!(EINVAL);
48    }
49    Ok(zx::Duration::from_seconds(ts.tv_sec) + zx::Duration::from_nanos(ts.tv_nsec))
50}
51
52pub fn duration_from_timeval<T: zx::Timeline>(tv: timeval) -> Result<zx::Duration<T>, Errno> {
53    if tv.tv_usec < 0 || tv.tv_usec >= MICROS_PER_SECOND {
54        return error!(EDOM);
55    }
56    Ok(zx::Duration::from_seconds(tv.tv_sec) + zx::Duration::from_micros(tv.tv_usec))
57}
58
59pub fn duration_from_poll_timeout(timeout_ms: i32) -> Result<zx::MonotonicDuration, Errno> {
60    if timeout_ms == -1 {
61        return Ok(zx::MonotonicDuration::INFINITE);
62    }
63
64    if timeout_ms < 0 {
65        return error!(EINVAL);
66    }
67
68    Ok(zx::MonotonicDuration::from_millis(timeout_ms.into()))
69}
70
71pub fn itimerspec_from_itimerval(tv: itimerval) -> itimerspec {
72    itimerspec {
73        it_interval: timespec_from_timeval(tv.it_interval),
74        it_value: timespec_from_timeval(tv.it_value),
75    }
76}
77
78pub fn timespec_from_timeval(tv: timeval) -> timespec {
79    timespec { tv_sec: tv.tv_sec, tv_nsec: tv.tv_usec * 1000 }
80}
81
82pub fn time_from_timeval<T: zx::Timeline + Copy>(tv: timeval) -> Result<zx::Instant<T>, Errno> {
83    let duration = duration_from_timeval::<T>(tv)?;
84    if duration.into_nanos() < 0 { error!(EINVAL) } else { Ok(zx::Instant::ZERO + duration) }
85}
86
87/// Returns a `zx::SyntheticInstant` for the given `timespec`, treating the `timespec` as an absolute
88/// point in time (i.e., not relative to "now").
89pub fn time_from_timespec<T: zx::Timeline>(ts: timespec) -> Result<zx::Instant<T>, Errno> {
90    let duration = duration_from_timespec::<T>(ts)?;
91    Ok(zx::Instant::ZERO + duration)
92}
93
94pub fn timespec_is_zero(ts: timespec) -> bool {
95    ts.tv_sec == 0 && ts.tv_nsec == 0
96}
97
98/// Returns an `itimerspec` with `it_value` set to `deadline` and `it_interval` set to `interval`.
99pub fn itimerspec_from_deadline_interval<T: zx::Timeline>(
100    deadline: zx::Instant<T>,
101    interval: zx::MonotonicDuration,
102) -> itimerspec {
103    itimerspec {
104        it_interval: timespec_from_duration(interval),
105        it_value: timespec_from_time(deadline),
106    }
107}
108
109pub fn duration_to_scheduler_clock(duration: zx::MonotonicDuration) -> i64 {
110    duration.into_nanos() / NANOS_PER_SCHEDULER_TICK
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116
117    const NANOS_PER_MICRO: i64 = 1000;
118
119    #[::fuchsia::test]
120    fn test_itimerspec() {
121        let deadline = zx::MonotonicInstant::from_nanos(2 * NANOS_PER_SECOND + 50);
122        let interval = zx::MonotonicDuration::from_nanos(1000);
123        let time_spec = itimerspec_from_deadline_interval(deadline, interval);
124        assert_eq!(time_spec.it_value.tv_sec, 2);
125        assert_eq!(time_spec.it_value.tv_nsec, 50);
126        assert_eq!(time_spec.it_interval.tv_nsec, 1000);
127    }
128
129    #[::fuchsia::test]
130    fn test_time_from_timespec() {
131        let time_spec = timespec { tv_sec: 100, tv_nsec: 100 };
132        let time: zx::MonotonicInstant =
133            time_from_timespec(time_spec).expect("failed to create time from time spec");
134        assert_eq!(time.into_nanos(), 100 * NANOS_PER_SECOND + 100);
135    }
136
137    #[::fuchsia::test]
138    fn test_invalid_time_from_timespec() {
139        let time_spec = timespec { tv_sec: 100, tv_nsec: NANOS_PER_SECOND * 2 };
140        assert_eq!(time_from_timespec::<zx::MonotonicTimeline>(time_spec), error!(EINVAL));
141
142        let time_spec = timespec { tv_sec: 1, tv_nsec: -1 };
143        assert_eq!(time_from_timespec::<zx::MonotonicTimeline>(time_spec), error!(EINVAL));
144
145        let time_spec = timespec { tv_sec: -1, tv_nsec: 1 };
146        assert_eq!(time_from_timespec::<zx::MonotonicTimeline>(time_spec), error!(EINVAL));
147    }
148
149    #[::fuchsia::test]
150    fn test_time_from_timeval() {
151        let tv = timeval { tv_sec: 100, tv_usec: 100 };
152        let time: zx::MonotonicInstant =
153            time_from_timeval(tv).expect("failed to create time from time spec");
154        assert_eq!(time.into_nanos(), 100 * NANOS_PER_SECOND + 100 * NANOS_PER_MICRO);
155    }
156
157    #[::fuchsia::test]
158    fn test_invalid_time_from_timeval() {
159        let tv = timeval { tv_sec: 100, tv_usec: MICROS_PER_SECOND * 2 };
160        assert_eq!(time_from_timeval::<zx::MonotonicTimeline>(tv), error!(EDOM));
161
162        let tv = timeval { tv_sec: 1, tv_usec: -1 };
163        assert_eq!(time_from_timeval::<zx::MonotonicTimeline>(tv), error!(EDOM));
164
165        let tv = timeval { tv_sec: -1, tv_usec: 1 };
166        assert_eq!(time_from_timeval::<zx::MonotonicTimeline>(tv), error!(EINVAL));
167    }
168}