os_clock/lib.rs
1//! Access various operating system clocks (such as per-thread CPU Time, system clock, monotomic, etc) on Unix-family systems.
2//!
3//! ## Thread clocks:
4//!
5//! Sendable per-thread CPU clocks are unique to this crate:
6//!
7//! ```
8//! # use std::time::Duration;
9//! # use os_clock::{self, Clock};
10//! #
11//! let clock = os_clock::cpu_clock_for_current_thread().unwrap();
12//!
13//! let start_time = clock.get_time().unwrap();
14//! // Do some work for 5ms...
15//! # loop {
16//! # if clock.get_time().unwrap() > (start_time + Duration::from_millis(5)) {
17//! # break;
18//! # }
19//! # }
20//! assert!(clock.get_time().unwrap() > start_time + Duration::from_millis(5));
21//!
22//! // Notably, a clock for the CPU time of one thread can be accessed from another thread:
23//! std::thread::spawn(move || {
24//! assert!(clock.get_time().unwrap() > Duration::from_millis(5));
25//!
26//! let self_clock = os_clock::cpu_clock_for_current_thread().unwrap();
27//! assert!(self_clock.get_time().unwrap() < Duration::from_millis(1));
28//! })
29//! .join()
30//! # .unwrap();
31//!
32//! // Clocks count from the thread's spawn time
33//! let new_clock = os_clock::cpu_clock_for_current_thread().unwrap();
34//! assert!(new_clock.get_time().unwrap() > Duration::from_millis(5));
35//!
36//! // Use a timer to start counting from the moment the timer is created
37//! let timer = new_clock.start_timer().unwrap();
38//! assert!(timer.elapsed().unwrap() < Duration::from_millis(1));
39//! // Do some work for 5ms...
40//! # loop {
41//! # if timer.elapsed().unwrap() > Duration::from_millis(5) {
42//! # break;
43//! # }
44//! # }
45//! assert!(timer.elapsed().unwrap() > Duration::from_millis(5));
46//!
47//! ```
48
49use std::io::Result;
50use std::time::Duration;
51
52#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "mach/mod.rs")]
53#[allow(unused_attributes)] // in order to allow #[path = "pthread.rs"] to work
54#[path = "pthread.rs"]
55mod os;
56
57pub use os::{cpu_clock_for_current_thread, ThreadCPUClock};
58
59#[cfg(any(target_os = "macos", target_os = "ios"))]
60pub use os::Thread;
61
62mod posix_clock;
63mod timer;
64pub use posix_clock::{
65 get_current_thread_cpu_time, PosixClock, MONOTONIC_CLOCK, PROCESS_CLOCK, REALTIME_CLOCK,
66};
67
68pub use timer::Timer;
69
70pub trait Clock: Sized + Send {
71 /// Get the current time value of the clock.
72 ///
73 /// Note that the meaning of the `Duration` differs depending on implementation.
74 /// Sometimes the clock represents CPU time, sometimes wall time, etc.
75 fn get_time(&self) -> Result<Duration>;
76
77 /// Start a timer at the current time
78 fn start_timer<'s>(&'s self) -> Result<Timer<'s, Self>> {
79 Ok(Timer {
80 clock: &self,
81 start: self.get_time()?,
82 })
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::{cpu_clock_for_current_thread, Clock};
89
90 #[test]
91 fn valid_measurement() {
92 let clock = cpu_clock_for_current_thread().unwrap();
93
94 let mut samples = std::iter::repeat::<()>(())
95 .map(|_| clock.get_time().unwrap())
96 .step_by(50000);
97
98 let mut last_time = samples.next().unwrap();
99
100 let samples = samples
101 .take(5)
102 .map(|this_time| {
103 assert!(this_time > last_time);
104 let diff = (this_time - last_time).as_secs_f64();
105 last_time = this_time;
106 diff
107 })
108 .collect::<Vec<f64>>();
109
110 let avg = samples.iter().sum::<f64>() / (samples.len() as f64);
111
112 let mean_abs_dev_scaled = samples
113 .iter()
114 .map(|sample| (sample - avg).abs())
115 .sum::<f64>()
116 / (samples.len() as f64)
117 / avg;
118
119 println!(
120 "
121durations of timing 50000 samples
122==================================
123{:#?}
124----------------------------------
125avg: {}, mad scaled: {}",
126 samples, avg, mean_abs_dev_scaled
127 );
128
129 assert!(mean_abs_dev_scaled < 0.1); // test that samples are on average within 10% of the mean
130 }
131}