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}