Skip to content

Commit 8aa4088

Browse files
committed
std: move time implementations to sys (Windows)
Windows has a similar problem as UEFI: some internals are also used for implementing other things. Thus I've left everything except for the actual `Instant` and `SystemTime` implementations inside the PAL. I've also taken the liberty of improving the code clarity a bit: there were a number of manual `SystemTime` conversions (including one that masked an `i64` to 32-bits before casting to `u32`, yikes) that now just call `from_intervals`. Also, defining a `PerformanceCounterInstant` just to convert it to a regular `Instant` immediately doesn't really make sense to me. I've thus changed the `perf_counter` module to contain only free functions that wrap system functionality. The actual conversion now happens in `Instant::now`.
1 parent 9805307 commit 8aa4088

3 files changed

Lines changed: 183 additions & 193 deletions

File tree

library/std/src/sys/pal/windows/time.rs

Lines changed: 22 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,214 +1,36 @@
1-
use core::hash::{Hash, Hasher};
2-
use core::ops::Neg;
3-
4-
use crate::cmp::Ordering;
1+
use crate::ops::Neg;
52
use crate::ptr::null;
6-
use crate::sys::{IntoInner, c};
3+
use crate::sys::pal::c;
74
use crate::time::Duration;
8-
use crate::{fmt, mem};
95

106
const NANOS_PER_SEC: u64 = 1_000_000_000;
11-
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
12-
13-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
14-
pub struct Instant {
15-
// This duration is relative to an arbitrary microsecond epoch
16-
// from the winapi QueryPerformanceCounter function.
17-
t: Duration,
18-
}
19-
20-
#[derive(Copy, Clone)]
21-
pub struct SystemTime {
22-
t: c::FILETIME,
23-
}
24-
25-
const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
26-
27-
pub const UNIX_EPOCH: SystemTime = SystemTime {
28-
t: c::FILETIME {
29-
dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32,
30-
dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32,
31-
},
32-
};
33-
34-
impl Instant {
35-
pub fn now() -> Instant {
36-
// High precision timing on windows operates in "Performance Counter"
37-
// units, as returned by the WINAPI QueryPerformanceCounter function.
38-
// These relate to seconds by a factor of QueryPerformanceFrequency.
39-
// In order to keep unit conversions out of normal interval math, we
40-
// measure in QPC units and immediately convert to nanoseconds.
41-
perf_counter::PerformanceCounterInstant::now().into()
42-
}
43-
44-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
45-
// On windows there's a threshold below which we consider two timestamps
46-
// equivalent due to measurement error. For more details + doc link,
47-
// check the docs on epsilon.
48-
let epsilon = perf_counter::PerformanceCounterInstant::epsilon();
49-
if other.t > self.t && other.t - self.t <= epsilon {
50-
Some(Duration::new(0, 0))
51-
} else {
52-
self.t.checked_sub(other.t)
53-
}
54-
}
55-
56-
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
57-
Some(Instant { t: self.t.checked_add(*other)? })
58-
}
59-
60-
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
61-
Some(Instant { t: self.t.checked_sub(*other)? })
62-
}
63-
}
64-
65-
impl SystemTime {
66-
pub const MAX: SystemTime = SystemTime {
67-
t: c::FILETIME {
68-
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
69-
dwHighDateTime: (i64::MAX >> 32) as u32,
70-
},
71-
};
72-
73-
pub const MIN: SystemTime =
74-
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };
75-
76-
pub fn now() -> SystemTime {
77-
unsafe {
78-
let mut t: SystemTime = mem::zeroed();
79-
c::GetSystemTimePreciseAsFileTime(&mut t.t);
80-
t
81-
}
82-
}
83-
84-
fn from_intervals(intervals: i64) -> SystemTime {
85-
SystemTime {
86-
t: c::FILETIME {
87-
dwLowDateTime: intervals as u32,
88-
dwHighDateTime: (intervals >> 32) as u32,
89-
},
90-
}
91-
}
92-
93-
fn intervals(&self) -> i64 {
94-
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
95-
}
96-
97-
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
98-
let me = self.intervals();
99-
let other = other.intervals();
100-
if me >= other {
101-
Ok(intervals2dur((me - other) as u64))
102-
} else {
103-
Err(intervals2dur((other - me) as u64))
104-
}
105-
}
106-
107-
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
108-
let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
109-
Some(SystemTime::from_intervals(intervals))
110-
}
111-
112-
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
113-
// Windows does not support times before 1601, hence why we don't
114-
// support negatives. In order to tackle this, we try to convert the
115-
// resulting value into an u64, which should obviously fail in the case
116-
// that the value is below zero.
117-
let intervals: u64 =
118-
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
119-
Some(SystemTime::from_intervals(intervals as i64))
120-
}
121-
}
122-
123-
impl PartialEq for SystemTime {
124-
fn eq(&self, other: &SystemTime) -> bool {
125-
self.intervals() == other.intervals()
126-
}
127-
}
128-
129-
impl Eq for SystemTime {}
130-
131-
impl PartialOrd for SystemTime {
132-
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
133-
Some(self.cmp(other))
134-
}
135-
}
7+
pub const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
1368

137-
impl Ord for SystemTime {
138-
fn cmp(&self, other: &SystemTime) -> Ordering {
139-
self.intervals().cmp(&other.intervals())
140-
}
141-
}
142-
143-
impl fmt::Debug for SystemTime {
144-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145-
f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish()
146-
}
147-
}
148-
149-
impl From<c::FILETIME> for SystemTime {
150-
fn from(t: c::FILETIME) -> SystemTime {
151-
SystemTime { t }
152-
}
153-
}
154-
155-
impl IntoInner<c::FILETIME> for SystemTime {
156-
fn into_inner(self) -> c::FILETIME {
157-
self.t
158-
}
159-
}
160-
161-
impl Hash for SystemTime {
162-
fn hash<H: Hasher>(&self, state: &mut H) {
163-
self.intervals().hash(state)
164-
}
165-
}
166-
167-
fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
9+
pub fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
16810
dur.as_secs()
16911
.checked_mul(INTERVALS_PER_SEC)?
17012
.checked_add(dur.subsec_nanos() as u64 / 100)?
17113
.try_into()
17214
.ok()
17315
}
17416

175-
fn intervals2dur(intervals: u64) -> Duration {
17+
pub fn intervals2dur(intervals: u64) -> Duration {
17618
Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32)
17719
}
17820

179-
mod perf_counter {
21+
pub mod perf_counter {
18022
use super::NANOS_PER_SEC;
18123
use crate::sync::atomic::{Atomic, AtomicU64, Ordering};
182-
use crate::sys::helpers::mul_div_u64;
18324
use crate::sys::{c, cvt};
18425
use crate::time::Duration;
18526

186-
pub struct PerformanceCounterInstant {
187-
ts: i64,
188-
}
189-
impl PerformanceCounterInstant {
190-
pub fn now() -> Self {
191-
Self { ts: query() }
192-
}
193-
194-
// Per microsoft docs, the margin of error for cross-thread time comparisons
195-
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
196-
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
197-
// /acquiring-high-resolution-time-stamps
198-
pub fn epsilon() -> Duration {
199-
let epsilon = NANOS_PER_SEC / (frequency() as u64);
200-
Duration::from_nanos(epsilon)
201-
}
202-
}
203-
impl From<PerformanceCounterInstant> for super::Instant {
204-
fn from(other: PerformanceCounterInstant) -> Self {
205-
let freq = frequency() as u64;
206-
let instant_nsec = mul_div_u64(other.ts as u64, NANOS_PER_SEC, freq);
207-
Self { t: Duration::from_nanos(instant_nsec) }
208-
}
27+
pub fn now() -> i64 {
28+
let mut qpc_value: i64 = 0;
29+
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
30+
qpc_value
20931
}
21032

211-
fn frequency() -> i64 {
33+
pub fn frequency() -> i64 {
21234
// Either the cached result of `QueryPerformanceFrequency` or `0` for
21335
// uninitialized. Storing this as a single `AtomicU64` allows us to use
21436
// `Relaxed` operations, as we are only interested in the effects on a
@@ -230,17 +52,21 @@ mod perf_counter {
23052
frequency
23153
}
23254

233-
fn query() -> i64 {
234-
let mut qpc_value: i64 = 0;
235-
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
236-
qpc_value
55+
// Per microsoft docs, the margin of error for cross-thread time comparisons
56+
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
57+
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
58+
// /acquiring-high-resolution-time-stamps
59+
pub fn epsilon() -> Duration {
60+
let epsilon = NANOS_PER_SEC / (frequency() as u64);
61+
Duration::from_nanos(epsilon)
23762
}
23863
}
23964

24065
/// A timer you can wait on.
24166
pub(crate) struct WaitableTimer {
24267
handle: c::HANDLE,
24368
}
69+
24470
impl WaitableTimer {
24571
/// Creates a high-resolution timer. Will fail before Windows 10, version 1803.
24672
pub fn high_resolution() -> Result<Self, ()> {
@@ -254,6 +80,7 @@ impl WaitableTimer {
25480
};
25581
if !handle.is_null() { Ok(Self { handle }) } else { Err(()) }
25682
}
83+
25784
pub fn set(&self, duration: Duration) -> Result<(), ()> {
25885
// Convert the Duration to a format similar to FILETIME.
25986
// Negative values are relative times whereas positive values are absolute.
@@ -262,11 +89,13 @@ impl WaitableTimer {
26289
let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) };
26390
if result != 0 { Ok(()) } else { Err(()) }
26491
}
92+
26593
pub fn wait(&self) -> Result<(), ()> {
26694
let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) };
26795
if result != c::WAIT_FAILED { Ok(()) } else { Err(()) }
26896
}
26997
}
98+
27099
impl Drop for WaitableTimer {
271100
fn drop(&mut self) {
272101
unsafe { c::CloseHandle(self.handle) };

library/std/src/sys/time/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ cfg_select! {
2424
pub use super::unsupported::{SystemTime, UNIX_EPOCH};
2525
}
2626
}
27+
target_os = "windows" => {
28+
mod windows;
29+
use windows as imp;
30+
}
2731
target_os = "xous" => {
2832
mod xous;
2933
use xous as imp;

0 commit comments

Comments
 (0)