Skip to content

Commit 5dd001b

Browse files
committed
Fix overflow in precise_time_ns() on Windows,
which starts happening after ~2 hours of machine uptime.
1 parent 0ef56da commit 5dd001b

File tree

1 file changed

+25
-2
lines changed

1 file changed

+25
-2
lines changed

src/libstd/sys/windows/time.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use ops::Sub;
1212
use time::Duration;
1313
use sync::{Once, ONCE_INIT};
1414

15+
const NANOS_PER_SEC: i64 = 1_000_000_000;
16+
1517
pub struct SteadyTime {
1618
t: libc::LARGE_INTEGER,
1719
}
@@ -24,7 +26,7 @@ impl SteadyTime {
2426
}
2527

2628
pub fn ns(&self) -> u64 {
27-
self.t as u64 * 1_000_000_000 / frequency() as u64
29+
mul_div_i64(self.t as i64, NANOS_PER_SEC, frequency() as i64) as u64
2830
}
2931
}
3032

@@ -45,6 +47,27 @@ impl<'a> Sub for &'a SteadyTime {
4547

4648
fn sub(self, other: &SteadyTime) -> Duration {
4749
let diff = self.t as i64 - other.t as i64;
48-
Duration::microseconds(diff * 1_000_000 / frequency() as i64)
50+
Duration::nanoseconds(mul_div_i64(diff, NANOS_PER_SEC, frequency() as i64))
4951
}
5052
}
53+
54+
// Computes (value*numer)/denom without overflow, as long as both
55+
// (numer*denom) and the overall result fit into i64 (which is the case
56+
// for our time conversions).
57+
fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 {
58+
let q = value / denom;
59+
let r = value % denom;
60+
// Decompose value as (value/denom*denom + value%denom),
61+
// substitute into (value*numer)/denom and simplify.
62+
// r < denom, so (denom*numer) is the upper bound of (r*numer)
63+
q * numer + r * numer / denom
64+
}
65+
66+
#[test]
67+
fn test_muldiv() {
68+
assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
69+
assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), -1_000_000_000_001_000);
70+
assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000), 1_000_000_000_001_000);
71+
assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), -1_000_000_000_001_000);
72+
assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000), 1_000_000_000_001_000);
73+
}

0 commit comments

Comments
 (0)