Skip to content

Commit

Permalink
Fix overflow in precise_time_ns() on Windows,
Browse files Browse the repository at this point in the history
which starts happening after ~2 hours of machine uptime.
  • Loading branch information
vadimcn committed Feb 25, 2015
1 parent 0ef56da commit 5dd001b
Showing 1 changed file with 25 additions and 2 deletions.
27 changes: 25 additions & 2 deletions src/libstd/sys/windows/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use ops::Sub;
use time::Duration;
use sync::{Once, ONCE_INIT};

const NANOS_PER_SEC: i64 = 1_000_000_000;

pub struct SteadyTime {
t: libc::LARGE_INTEGER,
}
Expand All @@ -24,7 +26,7 @@ impl SteadyTime {
}

pub fn ns(&self) -> u64 {
self.t as u64 * 1_000_000_000 / frequency() as u64
mul_div_i64(self.t as i64, NANOS_PER_SEC, frequency() as i64) as u64
}
}

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

fn sub(self, other: &SteadyTime) -> Duration {
let diff = self.t as i64 - other.t as i64;
Duration::microseconds(diff * 1_000_000 / frequency() as i64)
Duration::nanoseconds(mul_div_i64(diff, NANOS_PER_SEC, frequency() as i64))
}
}

// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).
fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 {
let q = value / denom;
let r = value % denom;
// Decompose value as (value/denom*denom + value%denom),
// substitute into (value*numer)/denom and simplify.
// r < denom, so (denom*numer) is the upper bound of (r*numer)
q * numer + r * numer / denom
}

#[test]
fn test_muldiv() {
assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), -1_000_000_000_001_000);
assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000), 1_000_000_000_001_000);
assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), -1_000_000_000_001_000);
assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000), 1_000_000_000_001_000);
}

0 comments on commit 5dd001b

Please sign in to comment.