1 //! Sleep, query system clocks, and set system clock
2 use crate::sys::time::TimeSpec;
3 #[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
4 #[cfg(feature = "process")]
5 use crate::unistd::Pid;
6 use crate::{Errno, Result};
7 use libc::{self, clockid_t};
8 use 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)]
14 pub struct ClockId(clockid_t);
15 
16 impl ClockId {
17     /// Creates `ClockId` from raw `clockid_t`
from_raw(clk_id: clockid_t) -> Self18     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"))]
res(self) -> Result<TimeSpec>33     pub fn res(self) -> Result<TimeSpec> {
34         clock_getres(self)
35     }
36 
37     /// Returns the current time on the clock id
now(self) -> Result<TimeSpec>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     )))]
set_time(self, timespec: TimeSpec) -> Result<()>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`
as_raw(self) -> clockid_t55     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(target_os = "linux", target_env = "musl")
126     ))]
127     pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
128     /// International Atomic Time.
129     ///
130     /// A nonsettable system-wide clock derived from wall-clock time but ignoring leap seconds.
131     #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
132     pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
133     #[cfg(any(
134         linux_android,
135         apple_targets,
136         freebsdlike,
137         target_os = "emscripten",
138         target_os = "fuchsia",
139     ))]
140     /// Returns the execution time of the calling thread.
141     pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
142         ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
143     #[cfg(freebsdlike)]
144     /// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
145     /// machine is running.
146     pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
147     #[cfg(freebsdlike)]
148     /// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for execution time at the expense of accuracy.
149     pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
150     #[cfg(freebsdlike)]
151     /// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for accuracy at the expense of execution time.
152     pub const CLOCK_UPTIME_PRECISE: ClockId =
153         ClockId(libc::CLOCK_UPTIME_PRECISE);
154     #[cfg(freebsdlike)]
155     /// Increments only when the CPU is running in user mode on behalf of the calling process.
156     pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
157 }
158 
159 impl From<ClockId> for clockid_t {
from(clock_id: ClockId) -> Self160     fn from(clock_id: ClockId) -> Self {
161         clock_id.as_raw()
162     }
163 }
164 
165 impl From<clockid_t> for ClockId {
from(clk_id: clockid_t) -> Self166     fn from(clk_id: clockid_t) -> Self {
167         ClockId::from_raw(clk_id)
168     }
169 }
170 
171 impl std::fmt::Display for ClockId {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result172     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
173         std::fmt::Display::fmt(&self.0, f)
174     }
175 }
176 
177 /// Get the resolution of the specified clock, (see
178 /// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
179 #[cfg(not(target_os = "redox"))]
clock_getres(clock_id: ClockId) -> Result<TimeSpec>180 pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
181     let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
182     let ret =
183         unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
184     Errno::result(ret)?;
185     let res = unsafe { c_time.assume_init() };
186     Ok(TimeSpec::from(res))
187 }
188 
189 /// Get the time of the specified clock, (see
190 /// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
clock_gettime(clock_id: ClockId) -> Result<TimeSpec>191 pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
192     let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
193     let ret =
194         unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
195     Errno::result(ret)?;
196     let res = unsafe { c_time.assume_init() };
197     Ok(TimeSpec::from(res))
198 }
199 
200 /// Set the time of the specified clock, (see
201 /// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
202 #[cfg(not(any(
203     target_os = "ios",
204     target_os = "tvos",
205     target_os = "watchos",
206     target_os = "redox",
207     target_os = "hermit"
208 )))]
clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()>209 pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
210     let ret =
211         unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
212     Errno::result(ret).map(drop)
213 }
214 
215 /// Get the clock id of the specified process id, (see
216 /// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
217 #[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
218 #[cfg(feature = "process")]
219 #[cfg_attr(docsrs, doc(cfg(feature = "process")))]
clock_getcpuclockid(pid: Pid) -> Result<ClockId>220 pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
221     let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
222     let ret =
223         unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
224     if ret == 0 {
225         let res = unsafe { clk_id.assume_init() };
226         Ok(ClockId::from(res))
227     } else {
228         Err(Errno::from_raw(ret))
229     }
230 }
231 
232 #[cfg(any(
233     linux_android,
234     solarish,
235     freebsdlike,
236     target_os = "netbsd",
237     target_os = "hurd",
238     target_os = "aix"
239 ))]
240 libc_bitflags! {
241     /// Flags that are used for arming the timer.
242     pub struct ClockNanosleepFlags: libc::c_int {
243         /// Indicates that a requested time value should be treated as absolute instead of
244         /// relative.
245         TIMER_ABSTIME;
246     }
247 }
248 
249 /// Suspend execution of this thread for the amount of time specified by `request`
250 /// and measured against the clock speficied by `clock_id`.
251 ///
252 /// If `flags` is [`TIMER_ABSTIME`](ClockNanosleepFlags::TIMER_ABSTIME), this function will suspend
253 /// execution until the time value of clock_id reaches the absolute time specified by `request`. If
254 /// a signal is caught by a signal-catching function, or a signal causes the process to terminate,
255 /// this sleep is interrrupted.
256 ///
257 /// see also [man 3 clock_nanosleep](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_nanosleep.html)
258 #[cfg(any(
259     linux_android,
260     solarish,
261     freebsdlike,
262     target_os = "netbsd",
263     target_os = "hurd",
264     target_os = "aix"
265 ))]
clock_nanosleep( clock_id: ClockId, flags: ClockNanosleepFlags, request: &TimeSpec, ) -> Result<TimeSpec>266 pub fn clock_nanosleep(
267     clock_id: ClockId,
268     flags: ClockNanosleepFlags,
269     request: &TimeSpec,
270 ) -> Result<TimeSpec> {
271     let mut remain = TimeSpec::new(0, 0);
272     let ret = unsafe {
273         libc::clock_nanosleep(
274             clock_id.as_raw(),
275             flags.bits(),
276             request.as_ref() as *const _,
277             remain.as_mut() as *mut _,
278         )
279     };
280     if ret == 0 {
281         Ok(remain)
282     } else {
283         Err(Errno::from_raw(ret))
284     }
285 }
286