1
1
use cmp:: Ordering ;
2
2
use fmt;
3
3
use mem;
4
- use sync:: Once ;
5
4
use sys:: c;
6
- use sys:: cvt;
7
- use sys_common:: mul_div_u64;
8
5
use time:: Duration ;
9
6
use convert:: TryInto ;
10
7
use core:: hash:: { Hash , Hasher } ;
@@ -14,7 +11,7 @@ const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
14
11
15
12
#[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Debug , Hash ) ]
16
13
pub struct Instant {
17
- t : c :: LARGE_INTEGER ,
14
+ t : u64 ,
18
15
}
19
16
20
17
#[ derive( Copy , Clone ) ]
@@ -33,11 +30,12 @@ pub const UNIX_EPOCH: SystemTime = SystemTime {
33
30
34
31
impl Instant {
35
32
pub fn now ( ) -> Instant {
36
- let mut t = Instant { t : 0 } ;
37
- cvt ( unsafe {
38
- c:: QueryPerformanceCounter ( & mut t. t )
39
- } ) . unwrap ( ) ;
40
- t
33
+ // High precision timing on windows operates in "Performance Counter"
34
+ // units, as returned by the WINAPI QueryPerformanceCounter function.
35
+ // These relate to seconds by a factor of QueryPerformanceFrequency.
36
+ // In order to keep unit conversions out of normal interval math, we
37
+ // measure in QPC units and immediately convert to nanoseconds.
38
+ perf_counter:: PerformanceCounterInstant :: now ( ) . into ( )
41
39
}
42
40
43
41
pub fn actually_monotonic ( ) -> bool {
@@ -49,43 +47,36 @@ impl Instant {
49
47
}
50
48
51
49
pub fn sub_instant ( & self , other : & Instant ) -> Duration {
52
- // Values which are +- 1 need to be considered as basically the same
53
- // units in time due to various measurement oddities, according to
54
- // Windows [1]
55
- //
56
- // [1]:
57
- // https://msdn.microsoft.com/en-us/library/windows/desktop
58
- // /dn553408%28v=vs.85%29.aspx#guidance
59
- if other. t > self . t && other. t - self . t == 1 {
50
+ // On windows there's a threshold below which we consider two timestamps
51
+ // equivalent due to measurement error. For more details + doc link,
52
+ // check the docs on epsilon_nanos.
53
+ let epsilon_ns =
54
+ perf_counter:: PerformanceCounterInstant :: epsilon_nanos ( ) as u64 ;
55
+ if other. t > self . t && other. t - self . t <= epsilon_ns {
60
56
return Duration :: new ( 0 , 0 )
61
57
}
62
- let diff = ( self . t as u64 ) . checked_sub ( other. t as u64 )
63
- . expect ( "specified instant was later than \
64
- self") ;
65
- let nanos = mul_div_u64 ( diff, NANOS_PER_SEC , frequency ( ) as u64 ) ;
66
- Duration :: new ( nanos / NANOS_PER_SEC , ( nanos % NANOS_PER_SEC ) as u32 )
58
+ let diff = ( self . t ) . checked_sub ( other. t )
59
+ . expect ( "specified instant was later than self" ) ;
60
+ Duration :: new ( diff / NANOS_PER_SEC , ( diff % NANOS_PER_SEC ) as u32 )
67
61
}
68
62
69
63
pub fn checked_add_duration ( & self , other : & Duration ) -> Option < Instant > {
70
- let freq = frequency ( ) as u64 ;
71
- let t = other. as_secs ( )
72
- . checked_mul ( freq) ?
73
- . checked_add ( mul_div_u64 ( other. subsec_nanos ( ) as u64 , freq, NANOS_PER_SEC ) ) ?
64
+ let sum = other. as_secs ( )
65
+ . checked_mul ( NANOS_PER_SEC ) ?
66
+ . checked_add ( other. subsec_nanos ( ) as u64 ) ?
74
67
. checked_add ( self . t as u64 ) ?;
75
68
Some ( Instant {
76
- t : t as c :: LARGE_INTEGER ,
69
+ t : sum ,
77
70
} )
78
71
}
79
72
80
73
pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < Instant > {
81
- let freq = frequency ( ) as u64 ;
82
- let t = other. as_secs ( ) . checked_mul ( freq) . and_then ( |i| {
83
- ( self . t as u64 ) . checked_sub ( i)
84
- } ) . and_then ( |i| {
85
- i. checked_sub ( mul_div_u64 ( other. subsec_nanos ( ) as u64 , freq, NANOS_PER_SEC ) )
86
- } ) ?;
74
+ let other_ns = other. as_secs ( )
75
+ . checked_mul ( NANOS_PER_SEC ) ?
76
+ . checked_add ( other. subsec_nanos ( ) as u64 ) ?;
77
+ let difference = self . t . checked_sub ( other_ns) ?;
87
78
Some ( Instant {
88
- t : t as c :: LARGE_INTEGER ,
79
+ t : difference ,
89
80
} )
90
81
}
91
82
}
@@ -186,14 +177,59 @@ fn intervals2dur(intervals: u64) -> Duration {
186
177
( ( intervals % INTERVALS_PER_SEC ) * 100 ) as u32 )
187
178
}
188
179
189
- fn frequency ( ) -> c:: LARGE_INTEGER {
190
- static mut FREQUENCY : c:: LARGE_INTEGER = 0 ;
191
- static ONCE : Once = Once :: new ( ) ;
180
+ mod perf_counter {
181
+ use super :: { NANOS_PER_SEC } ;
182
+ use sync:: Once ;
183
+ use sys_common:: mul_div_u64;
184
+ use sys:: c;
185
+ use sys:: cvt;
186
+
187
+ pub struct PerformanceCounterInstant {
188
+ ts : c:: LARGE_INTEGER
189
+ }
190
+ impl PerformanceCounterInstant {
191
+ pub fn now ( ) -> Self {
192
+ Self {
193
+ ts : query ( )
194
+ }
195
+ }
192
196
193
- unsafe {
194
- ONCE . call_once ( || {
195
- cvt ( c:: QueryPerformanceFrequency ( & mut FREQUENCY ) ) . unwrap ( ) ;
196
- } ) ;
197
- FREQUENCY
197
+ // Per microsoft docs, the margin of error for cross-thread time comparisons
198
+ // using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
199
+ // Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
200
+ // /acquiring-high-resolution-time-stamps
201
+ pub fn epsilon_nanos ( ) -> u32 {
202
+ let epsilon = NANOS_PER_SEC / ( frequency ( ) as u64 ) ;
203
+ // As noted elsewhere, subsecond nanos always fit in a u32
204
+ epsilon as u32
205
+ }
206
+ }
207
+ impl From < PerformanceCounterInstant > for super :: Instant {
208
+ fn from ( other : PerformanceCounterInstant ) -> Self {
209
+ let freq = frequency ( ) as u64 ;
210
+ Self {
211
+ t : mul_div_u64 ( other. ts as u64 , NANOS_PER_SEC , freq)
212
+ }
213
+ }
214
+ }
215
+
216
+ fn frequency ( ) -> c:: LARGE_INTEGER {
217
+ static mut FREQUENCY : c:: LARGE_INTEGER = 0 ;
218
+ static ONCE : Once = Once :: new ( ) ;
219
+
220
+ unsafe {
221
+ ONCE . call_once ( || {
222
+ cvt ( c:: QueryPerformanceFrequency ( & mut FREQUENCY ) ) . unwrap ( ) ;
223
+ } ) ;
224
+ FREQUENCY
225
+ }
226
+ }
227
+
228
+ fn query ( ) -> c:: LARGE_INTEGER {
229
+ let mut qpc_value: c:: LARGE_INTEGER = 0 ;
230
+ cvt ( unsafe {
231
+ c:: QueryPerformanceCounter ( & mut qpc_value)
232
+ } ) . unwrap ( ) ;
233
+ qpc_value
198
234
}
199
235
}
0 commit comments