nix/
time.rs

1//! Sleep, query system clocks, and set system clock
2use crate::sys::time::TimeSpec;
3#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
4#[cfg(feature = "process")]
5use crate::unistd::Pid;
6use crate::{Errno, Result};
7use libc::{self, clockid_t};
8use std::mem::MaybeUninit;
9
10/// Clock identifier
11///
12/// Newtype pattern around [`libc::clockid_t`].
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
14pub struct ClockId(clockid_t);
15
16impl ClockId {
17    /// Creates `ClockId` from raw `clockid_t`
18    pub const fn from_raw(clk_id: clockid_t) -> Self {
19        ClockId(clk_id)
20    }
21
22    feature! {
23    #![feature = "process"]
24    /// Returns `ClockId` of a `pid` CPU-time clock
25    #[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
26    pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
27        clock_getcpuclockid(pid)
28    }
29    }
30
31    /// Returns resolution of the clock id
32    #[cfg(not(target_os = "redox"))]
33    pub fn res(self) -> Result<TimeSpec> {
34        clock_getres(self)
35    }
36
37    /// Returns the current time on the clock id
38    pub fn now(self) -> Result<TimeSpec> {
39        clock_gettime(self)
40    }
41
42    /// Sets time to `timespec` on the clock id
43    #[cfg(not(any(
44        target_os = "ios",
45        target_os = "tvos",
46        target_os = "watchos",
47        target_os = "redox",
48        target_os = "hermit"
49    )))]
50    pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
51        clock_settime(self, timespec)
52    }
53
54    /// Gets the raw `clockid_t` wrapped by `self`
55    pub const fn as_raw(self) -> clockid_t {
56        self.0
57    }
58
59    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
60    /// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
61    /// machine is running.
62    pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
63    /// Like [`CLOCK_BOOTTIME`](ClockId::CLOCK_BOOTTIME), but will wake the system if it is
64    /// suspended..
65    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
66    pub const CLOCK_BOOTTIME_ALARM: ClockId =
67        ClockId(libc::CLOCK_BOOTTIME_ALARM);
68    /// Increments in SI seconds.
69    pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
70    /// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
71    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
72    pub const CLOCK_MONOTONIC_COARSE: ClockId =
73        ClockId(libc::CLOCK_MONOTONIC_COARSE);
74    #[cfg(freebsdlike)]
75    /// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
76    pub const CLOCK_MONOTONIC_FAST: ClockId =
77        ClockId(libc::CLOCK_MONOTONIC_FAST);
78    #[cfg(freebsdlike)]
79    /// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for accuracy at the expense of execution time.
80    pub const CLOCK_MONOTONIC_PRECISE: ClockId =
81        ClockId(libc::CLOCK_MONOTONIC_PRECISE);
82    /// Similar to [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but provides access to a raw
83    /// hardware-based time that is not subject to NTP adjustments.
84    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
85    pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
86    #[cfg(any(
87        linux_android,
88        apple_targets,
89        freebsdlike,
90        target_os = "emscripten",
91        target_os = "fuchsia",
92        target_os = "redox",
93    ))]
94    /// Returns the execution time of the calling process.
95    pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
96        ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
97    #[cfg(freebsdlike)]
98    /// Increments when the CPU is running in user or kernel mode
99    pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
100    /// Increments as a wall clock should.
101    pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
102    /// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but not settable.
103    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
104    pub const CLOCK_REALTIME_ALARM: ClockId =
105        ClockId(libc::CLOCK_REALTIME_ALARM);
106    /// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
107    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
108    pub const CLOCK_REALTIME_COARSE: ClockId =
109        ClockId(libc::CLOCK_REALTIME_COARSE);
110    #[cfg(freebsdlike)]
111    /// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
112    pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
113    #[cfg(freebsdlike)]
114    /// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for accuracy at the expense of execution time.
115    pub const CLOCK_REALTIME_PRECISE: ClockId =
116        ClockId(libc::CLOCK_REALTIME_PRECISE);
117    #[cfg(freebsdlike)]
118    /// Returns the current second without performing a full time counter query, using an in-kernel
119    /// cached value of the current second.
120    pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
121    #[allow(missing_docs)] // Undocumented on Linux!
122    #[cfg(any(
123        target_os = "emscripten",
124        target_os = "fuchsia",
125        all(
126            target_os = "linux",
127            any(target_env = "musl", target_env = "ohos")
128        )
129    ))]
130    pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
131    /// International Atomic Time.
132    ///
133    /// A nonsettable system-wide clock derived from wall-clock time but ignoring leap seconds.
134    #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
135    pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
136    #[cfg(any(
137        linux_android,
138        apple_targets,
139        freebsdlike,
140        target_os = "emscripten",
141        target_os = "fuchsia",
142    ))]
143    /// Returns the execution time of the calling thread.
144    pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
145        ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
146    #[cfg(freebsdlike)]
147    /// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
148    /// machine is running.
149    pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
150    #[cfg(freebsdlike)]
151    /// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for execution time at the expense of accuracy.
152    pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
153    #[cfg(freebsdlike)]
154    /// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for accuracy at the expense of execution time.
155    pub const CLOCK_UPTIME_PRECISE: ClockId =
156        ClockId(libc::CLOCK_UPTIME_PRECISE);
157    #[cfg(freebsdlike)]
158    /// Increments only when the CPU is running in user mode on behalf of the calling process.
159    pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
160}
161
162impl From<ClockId> for clockid_t {
163    fn from(clock_id: ClockId) -> Self {
164        clock_id.as_raw()
165    }
166}
167
168impl From<clockid_t> for ClockId {
169    fn from(clk_id: clockid_t) -> Self {
170        ClockId::from_raw(clk_id)
171    }
172}
173
174impl std::fmt::Display for ClockId {
175    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
176        std::fmt::Display::fmt(&self.0, f)
177    }
178}
179
180/// Get the resolution of the specified clock, (see
181/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
182#[cfg(not(target_os = "redox"))]
183pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
184    let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
185    let ret =
186        unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
187    Errno::result(ret)?;
188    let res = unsafe { c_time.assume_init() };
189    Ok(TimeSpec::from(res))
190}
191
192/// Get the time of the specified clock, (see
193/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
194pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
195    let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
196    let ret =
197        unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
198    Errno::result(ret)?;
199    let res = unsafe { c_time.assume_init() };
200    Ok(TimeSpec::from(res))
201}
202
203/// Set the time of the specified clock, (see
204/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
205#[cfg(not(any(
206    target_os = "ios",
207    target_os = "tvos",
208    target_os = "watchos",
209    target_os = "redox",
210    target_os = "hermit"
211)))]
212pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
213    let ret =
214        unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
215    Errno::result(ret).map(drop)
216}
217
218/// Get the clock id of the specified process id, (see
219/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
220#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
221#[cfg(feature = "process")]
222#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
223pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
224    let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
225    let ret =
226        unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
227    if ret == 0 {
228        let res = unsafe { clk_id.assume_init() };
229        Ok(ClockId::from(res))
230    } else {
231        Err(Errno::from_raw(ret))
232    }
233}
234
235#[cfg(any(
236    linux_android,
237    solarish,
238    freebsdlike,
239    target_os = "netbsd",
240    target_os = "hurd",
241    target_os = "aix"
242))]
243libc_bitflags! {
244    /// Flags that are used for arming the timer.
245    pub struct ClockNanosleepFlags: libc::c_int {
246        /// Indicates that a requested time value should be treated as absolute instead of
247        /// relative.
248        TIMER_ABSTIME;
249    }
250}
251
252/// Suspend execution of this thread for the amount of time specified by `request`
253/// and measured against the clock speficied by `clock_id`.
254///
255/// If `flags` is [`TIMER_ABSTIME`](ClockNanosleepFlags::TIMER_ABSTIME), this function will suspend
256/// execution until the time value of clock_id reaches the absolute time specified by `request`. If
257/// a signal is caught by a signal-catching function, or a signal causes the process to terminate,
258/// this sleep is interrrupted.
259///
260/// see also [man 3 clock_nanosleep](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_nanosleep.html)
261#[cfg(any(
262    linux_android,
263    solarish,
264    freebsdlike,
265    target_os = "netbsd",
266    target_os = "hurd",
267    target_os = "aix"
268))]
269pub fn clock_nanosleep(
270    clock_id: ClockId,
271    flags: ClockNanosleepFlags,
272    request: &TimeSpec,
273) -> Result<TimeSpec> {
274    let mut remain = TimeSpec::new(0, 0);
275    let ret = unsafe {
276        libc::clock_nanosleep(
277            clock_id.as_raw(),
278            flags.bits(),
279            request.as_ref() as *const _,
280            remain.as_mut() as *mut _,
281        )
282    };
283    if ret == 0 {
284        Ok(remain)
285    } else {
286        Err(Errno::from_raw(ret))
287    }
288}