cpu_time/
windows.rs

1use std::io::Result;
2use std::marker::PhantomData;
3use std::rc::Rc;
4use std::time::Duration;
5
6use winapi::shared::minwindef::{BOOL, FILETIME};
7use winapi::um::processthreadsapi::{GetCurrentProcess, GetCurrentThread};
8use winapi::um::processthreadsapi::{GetProcessTimes, GetThreadTimes};
9
10/// CPU Time Used by The Whole Process
11///
12/// This is an opaque type similar to `std::time::Instant`.
13/// Use `elapsed()` or `duration_since()` to get meaningful time deltas.
14#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
15pub struct ProcessTime(Duration);
16
17/// CPU Time Used by The Current Thread
18///
19/// This is an opaque type similar to `std::time::Instant`.
20/// Use `elapsed()` or `duration_since()` to get meaningful time deltas.
21///
22/// This type is non-thread-shareable (!Sync, !Send) because otherwise it's
23/// to easy to mess up times from different threads. However, you can freely
24/// send Duration's returned by `elapsed()` and `duration_since()`.
25#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
26pub struct ThreadTime(
27    Duration,
28    // makes type non-sync and non-send
29    PhantomData<Rc<()>>,
30);
31
32fn to_duration(kernel_time: FILETIME, user_time: FILETIME) -> Duration {
33    // resolution: 100ns
34    let kns100 = ((kernel_time.dwHighDateTime as u64) << 32) + kernel_time.dwLowDateTime as u64;
35    let uns100 = ((user_time.dwHighDateTime as u64) << 32) + user_time.dwLowDateTime as u64;
36    return Duration::new(
37        (kns100 + uns100) / 10_000_000,
38        (((kns100 + uns100) * 100) % 1000_000_000) as u32,
39    );
40}
41
42fn zero() -> FILETIME {
43    FILETIME {
44        dwLowDateTime: 0,
45        dwHighDateTime: 0,
46    }
47}
48
49impl ProcessTime {
50    /// Get current CPU time used by a process
51    pub fn try_now() -> Result<Self> {
52        let mut kernel_time = zero();
53        let mut user_time = zero();
54        let process = unsafe { GetCurrentProcess() };
55        let ok = unsafe { GetProcessTimes(process,
56            &mut zero(), &mut zero(),
57            &mut kernel_time, &mut user_time) };
58        if ok == 0 {
59            return Err(std::io::Error::last_os_error());
60        }
61        Ok(Self(to_duration(kernel_time, user_time)))
62    }
63
64    /// Get current CPU time used by a process
65    ///
66    /// # Panics
67    ///
68    /// If GetProcessTimes fails. This may happen, for instance, in case of
69    /// insufficient permissions.
70    ///
71    /// See [this MSDN page][msdn] for details. If you prefer to handle such errors, consider
72    /// using `try_now`.
73    ///
74    /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
75    pub fn now() -> Self {
76        Self::try_now().expect("GetProcessTimes failed")
77    }
78
79    /// Returns the amount of CPU time used from the previous timestamp to now.
80    pub fn try_elapsed(&self) -> Result<Duration> {
81        Ok(Self::try_now()?.duration_since(*self))
82    }
83
84    /// Returns the amount of CPU time used from the previous timestamp to now.
85    ///
86    /// # Panics
87    ///
88    /// If `ProcessTime::now()` panics.
89    pub fn elapsed(&self) -> Duration {
90        Self::now().duration_since(*self)
91    }
92
93    /// Returns the amount of CPU time used from the previous timestamp.
94    pub fn duration_since(&self, timestamp: Self) -> Duration {
95        self.0 - timestamp.0
96    }
97
98    /// Returns the total amount of CPU time used from the program start.
99    pub fn as_duration(&self) -> Duration {
100        self.0
101    }
102}
103
104impl ThreadTime {
105    /// Get current CPU time used by a process process
106    pub fn try_now() -> Result<Self> {
107        let mut kernel_time = zero();
108        let mut user_time = zero();
109        let thread = unsafe { GetCurrentThread() };
110        let ok = unsafe { GetThreadTimes(thread,
111            &mut zero(), &mut zero(),
112            &mut kernel_time, &mut user_time) };
113        if ok == 0 {
114            return Err(std::io::Error::last_os_error());
115        }
116        Ok(Self(to_duration(kernel_time, user_time), PhantomData))
117    }
118
119    ///
120    /// # Panics
121    ///
122    /// If GetThreadTimes fails. This may happen, for instance, in case of
123    /// insufficient permissions.
124    ///
125    /// See [this MSDN page][msdn] for details. If you prefer to handle such errors, consider
126    /// using `try_now`.
127    ///
128    /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
129    pub fn now() -> Self {
130        Self::try_now().expect("GetThreadTimes failed")
131    }
132
133    /// Returns the amount of CPU time used from the previous timestamp to now.
134    pub fn try_elapsed(&self) -> Result<Duration> {
135        Ok(Self::try_now()?.duration_since(*self))
136    }
137
138    /// Returns the amount of CPU time used from the previous timestamp to now.
139    ///
140    /// # Panics
141    ///
142    /// If `ThreadTime::now()` panics.
143    pub fn elapsed(&self) -> Duration {
144        Self::now().duration_since(*self)
145    }
146
147    /// Returns the amount of CPU time used by the current thread
148    /// from the previous timestamp.
149    pub fn duration_since(&self, timestamp: ThreadTime) -> Duration {
150        self.0 - timestamp.0
151    }
152
153    /// Returns the total amount of CPU time used from the program start.
154    pub fn as_duration(&self) -> Duration {
155        self.0
156    }
157}