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