howlong/clock/
posix.rs

1// Ref: https://github.com/boostorg/chrono/tree/develop/include/boost/chrono/detail/inlined/posix
2
3use crate::{Clock, Duration, Error, ProcessTimePoint, Result, TimePoint};
4
5pub(crate) fn errno() -> i32 {
6    errno::errno().into()
7}
8
9/// A system clock.
10pub struct SystemClock;
11
12impl Clock for SystemClock {
13    type Output = TimePoint;
14
15    fn try_now() -> Result<Self::Output> {
16        let mut ts = libc::timespec {
17            tv_sec: 0,
18            tv_nsec: 0,
19        };
20        let ret = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts) };
21        if ret != 0 {
22            return Err(Error::SystemError("clock_gettime", errno()));
23        }
24        let d = Duration::from_secs(ts.tv_sec as u64) + Duration::from_nanos(ts.tv_nsec as u64);
25        Ok(TimePoint(d))
26    }
27}
28
29#[cfg(have_steady_clock)]
30#[doc = "A steady clock."]
31pub struct SteadyClock;
32
33#[cfg(have_steady_clock)]
34impl Clock for SteadyClock {
35    type Output = TimePoint;
36
37    fn try_now() -> Result<Self::Output> {
38        let mut ts = libc::timespec {
39            tv_sec: 0,
40            tv_nsec: 0,
41        };
42        let ret = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
43        if ret != 0 {
44            return Err(Error::SystemError("clock_gettime", errno()));
45        }
46        let d = Duration::from_secs(ts.tv_sec as u64) + Duration::from_nanos(ts.tv_nsec as u64);
47        Ok(TimePoint(d))
48    }
49}
50
51fn tick_factor() -> Result<u64> {
52    let factor = unsafe { libc::sysconf(libc::_SC_CLK_TCK) };
53    if factor <= 0 {
54        return Err(Error::SystemError("sysconf(_SC_CLK_TCK)", errno()));
55    }
56    if factor > 1_000_000_000 {
57        return Err(Error::ClkFreqTooHigh);
58    }
59    Ok((1_000_000_000 / factor) as u64)
60}
61
62#[inline(always)]
63fn times() -> Result<(libc::clock_t, libc::tms)> {
64    let mut tm = libc::tms {
65        tms_utime: 0,
66        tms_stime: 0,
67        tms_cutime: 0,
68        tms_cstime: 0,
69    };
70    let ret = unsafe { libc::times(&mut tm) };
71    if ret == -1i64 as libc::clock_t {
72        return Err(Error::SystemError("times", errno()));
73    }
74    Ok((ret, tm))
75}
76
77/// A clock to report the real process wall-clock.
78pub struct ProcessRealCPUClock;
79
80impl Clock for ProcessRealCPUClock {
81    type Output = TimePoint;
82
83    fn try_now() -> Result<Self::Output> {
84        let (c, _) = times()?;
85        let factor = tick_factor()?;
86        let d = Duration::from_nanos((c as u64) * factor);
87        Ok(TimePoint(d))
88    }
89}
90
91/// A clock to report the user cpu-clock.
92pub struct ProcessUserCPUClock;
93
94impl Clock for ProcessUserCPUClock {
95    type Output = TimePoint;
96
97    fn try_now() -> Result<Self::Output> {
98        let (_, tm) = times()?;
99        let factor = tick_factor()?;
100        let d = Duration::from_nanos(((tm.tms_utime + tm.tms_cutime) as u64) * factor);
101        Ok(TimePoint(d))
102    }
103}
104
105/// A clock to report the system cpu-clock.
106pub struct ProcessSystemCPUClock;
107
108impl Clock for ProcessSystemCPUClock {
109    type Output = TimePoint;
110
111    fn try_now() -> Result<Self::Output> {
112        let (_, tm) = times()?;
113        let factor = tick_factor()?;
114        let d = Duration::from_nanos(((tm.tms_stime + tm.tms_cstime) as u64) * factor);
115        Ok(TimePoint(d))
116    }
117}
118
119/// A clock to report real, user-CPU, and system-CPU clocks.
120pub struct ProcessCPUClock;
121
122impl Clock for ProcessCPUClock {
123    type Output = ProcessTimePoint;
124
125    fn try_now() -> Result<Self::Output> {
126        let (c, tm) = times()?;
127        let factor = tick_factor()?;
128        Ok(ProcessTimePoint {
129            real: Duration::from_nanos((c as u64) * factor),
130            user: Duration::from_nanos(((tm.tms_utime + tm.tms_cutime) as u64) * factor),
131            system: Duration::from_nanos(((tm.tms_stime + tm.tms_cstime) as u64) * factor),
132        })
133    }
134}
135
136/// A clock to report the real thread wall-clock.
137pub struct ThreadClock;
138
139#[cfg(not(have_clock_thread_cputime_id))]
140extern "C" {
141    fn pthread_getcpuclockid(
142        thread_id: libc::pthread_t,
143        clock_id: *mut libc::clockid_t,
144    ) -> libc::c_int;
145}
146
147#[cfg(not(have_clock_thread_cputime_id))]
148#[inline(always)]
149fn get_thread_clock_id() -> Result<libc::clockid_t> {
150    let mut clock_id: libc::clockid_t = 0;
151    let ret = unsafe { pthread_getcpuclockid(libc::pthread_self(), &mut clock_id) };
152    if ret != 0 {
153        return Err(Error::SystemError("pthread_getcpuclockid", errno()));
154    }
155    Ok(clock_id)
156}
157
158impl Clock for ThreadClock {
159    type Output = TimePoint;
160
161    fn try_now() -> Result<Self::Output> {
162        let mut ts = libc::timespec {
163            tv_sec: 0,
164            tv_nsec: 0,
165        };
166        #[cfg(have_clock_thread_cputime_id)]
167        let clock_id = libc::CLOCK_THREAD_CPUTIME_ID;
168        #[cfg(not(have_clock_thread_cputime_id))]
169        let clock_id = get_thread_clock_id()?;
170        let ret = unsafe { libc::clock_gettime(clock_id, &mut ts) };
171        if ret != 0 {
172            return Err(Error::SystemError("clock_gettime", errno()));
173        }
174        let d = Duration::from_secs(ts.tv_sec as u64) + Duration::from_nanos(ts.tv_nsec as u64);
175        Ok(TimePoint(d))
176    }
177}