Auto merge of #29894 - alexcrichton:stdtime, r=brson
This commit is an implementation of [RFC 1288][rfc] which adds two new unstable types to the `std::time` module. The `Instant` type is used to represent measurements of a monotonically increasing clock suitable for measuring time withing a process for operations such as benchmarks or just the elapsed time to do something. An `Instant` favors panicking when bugs are found as the bugs are programmer errors rather than typical errors that can be encountered. [rfc]: https://github.com/rust-lang/rfcs/pull/1288 The `SystemTime` type is used to represent a system timestamp and is not monotonic. Very few guarantees are provided about this measurement of the system clock, but a fixed point in time (`UNIX_EPOCH`) is provided to learn about the relative distance from this point for any particular time stamp. This PR takes the same implementation strategy as the `time` crate on crates.io, namely: | Platform | Instant | SystemTime | |------------|--------------------------|--------------------------| | Windows | QueryPerformanceCounter | GetSystemTimeAsFileTime | | OSX | mach_absolute_time | gettimeofday | | Unix | CLOCK_MONOTONIC | CLOCK_REALTIME | These implementations can perhaps be refined over time, but they currently satisfy the requirements of the `Instant` and `SystemTime` types while also being portable across implementations and revisions of each platform. cc #29866
This commit is contained in:
commit
b289892b5d
@ -12,11 +12,10 @@ use prelude::v1::*;
|
||||
|
||||
use sync::atomic::{AtomicUsize, Ordering};
|
||||
use sync::{mutex, MutexGuard, PoisonError};
|
||||
use sys::time::SteadyTime;
|
||||
use sys_common::condvar as sys;
|
||||
use sys_common::mutex as sys_mutex;
|
||||
use sys_common::poison::{self, LockResult};
|
||||
use time::Duration;
|
||||
use time::{Instant, Duration};
|
||||
|
||||
/// A type indicating whether a timed wait on a condition variable returned
|
||||
/// due to a time out or not.
|
||||
@ -345,14 +344,13 @@ impl StaticCondvar {
|
||||
where F: FnMut(LockResult<&mut T>) -> bool {
|
||||
// This could be made more efficient by pushing the implementation into
|
||||
// sys::condvar
|
||||
let start = SteadyTime::now();
|
||||
let start = Instant::now();
|
||||
let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard);
|
||||
while !f(guard_result
|
||||
.as_mut()
|
||||
.map(|g| &mut **g)
|
||||
.map_err(|e| PoisonError::new(&mut **e.get_mut()))) {
|
||||
let now = SteadyTime::now();
|
||||
let consumed = &now - &start;
|
||||
let consumed = start.elapsed();
|
||||
let guard = guard_result.unwrap_or_else(|e| e.into_inner());
|
||||
let (new_guard_result, timed_out) = if consumed > dur {
|
||||
(Ok(guard), WaitTimeoutResult(true))
|
||||
|
@ -98,3 +98,22 @@ pub fn cleanup() {
|
||||
at_exit_imp::cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
// 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).
|
||||
#[allow(dead_code)] // not used on all platforms
|
||||
pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
|
||||
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_u64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
|
||||
1_000_000_000_001_000);
|
||||
}
|
||||
|
@ -12,8 +12,7 @@ use cell::UnsafeCell;
|
||||
use libc;
|
||||
use ptr;
|
||||
use sys::mutex::{self, Mutex};
|
||||
use sys::time;
|
||||
use time::Duration;
|
||||
use time::{Instant, Duration};
|
||||
|
||||
pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
|
||||
|
||||
@ -53,7 +52,7 @@ impl Condvar {
|
||||
// stable time. pthread_cond_timedwait uses system time, but we want to
|
||||
// report timeout based on stable time.
|
||||
let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
|
||||
let stable_now = time::SteadyTime::now();
|
||||
let stable_now = Instant::now();
|
||||
let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
|
||||
debug_assert_eq!(r, 0);
|
||||
|
||||
@ -81,7 +80,7 @@ impl Condvar {
|
||||
|
||||
// ETIMEDOUT is not a totally reliable method of determining timeout due
|
||||
// to clock shifts, so do the check ourselves
|
||||
&time::SteadyTime::now() - &stable_now < dur
|
||||
stable_now.elapsed() < dur
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -8,28 +8,181 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use self::inner::SteadyTime;
|
||||
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
const NSEC_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
mod inner {
|
||||
use cmp::Ordering;
|
||||
use fmt;
|
||||
use libc;
|
||||
use time::Duration;
|
||||
use ops::Sub;
|
||||
use sync::Once;
|
||||
use super::NSEC_PER_SEC;
|
||||
use sync::Once;
|
||||
use sys::cvt;
|
||||
use sys_common::mul_div_u64;
|
||||
use time::Duration;
|
||||
|
||||
pub struct SteadyTime {
|
||||
const USEC_PER_SEC: u64 = NSEC_PER_SEC / 1000;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct Instant {
|
||||
t: u64
|
||||
}
|
||||
|
||||
impl SteadyTime {
|
||||
pub fn now() -> SteadyTime {
|
||||
SteadyTime {
|
||||
t: unsafe { libc::mach_absolute_time() },
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SystemTime {
|
||||
t: libc::timeval,
|
||||
}
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime {
|
||||
t: libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
},
|
||||
};
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
Instant { t: unsafe { libc::mach_absolute_time() } }
|
||||
}
|
||||
|
||||
pub fn sub_instant(&self, other: &Instant) -> Duration {
|
||||
let info = info();
|
||||
let diff = self.t.checked_sub(other.t)
|
||||
.expect("second instant is later than self");
|
||||
let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
|
||||
Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> Instant {
|
||||
Instant {
|
||||
t: self.t.checked_add(dur2intervals(other))
|
||||
.expect("overflow when adding duration to instant"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> Instant {
|
||||
Instant {
|
||||
t: self.t.checked_sub(dur2intervals(other))
|
||||
.expect("overflow when adding duration to instant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub fn now() -> SystemTime {
|
||||
let mut s = SystemTime {
|
||||
t: libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
},
|
||||
};
|
||||
cvt(unsafe {
|
||||
libc::gettimeofday(&mut s.t, 0 as *mut _)
|
||||
}).unwrap();
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime)
|
||||
-> Result<Duration, Duration> {
|
||||
if self >= other {
|
||||
Ok(if self.t.tv_usec >= other.t.tv_usec {
|
||||
Duration::new(self.t.tv_sec as u64 - other.t.tv_sec as u64,
|
||||
(self.t.tv_usec as u32 -
|
||||
other.t.tv_usec as u32) * 1000)
|
||||
} else {
|
||||
Duration::new(self.t.tv_sec as u64 - 1 - other.t.tv_sec as u64,
|
||||
(self.t.tv_usec as u32 + (USEC_PER_SEC as u32) -
|
||||
other.t.tv_usec as u32) * 1000)
|
||||
})
|
||||
} else {
|
||||
match other.sub_time(self) {
|
||||
Ok(d) => Err(d),
|
||||
Err(d) => Ok(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> SystemTime {
|
||||
let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
|
||||
let mut secs = secs.expect("overflow when adding duration to time");
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
// in a u32.
|
||||
let mut usec = (other.subsec_nanos() / 1000) + self.t.tv_usec as u32;
|
||||
if usec > USEC_PER_SEC as u32 {
|
||||
usec -= USEC_PER_SEC as u32;
|
||||
secs = secs.checked_add(1).expect("overflow when adding \
|
||||
duration to time");
|
||||
}
|
||||
SystemTime {
|
||||
t: libc::timeval {
|
||||
tv_sec: secs as libc::time_t,
|
||||
tv_usec: usec as libc::suseconds_t,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
|
||||
let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
|
||||
let mut secs = secs.expect("overflow when subtracting duration \
|
||||
from time");
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
let mut usec = self.t.tv_usec as i32 -
|
||||
(other.subsec_nanos() / 1000) as i32;
|
||||
if usec < 0 {
|
||||
usec += USEC_PER_SEC as i32;
|
||||
secs = secs.checked_sub(1).expect("overflow when subtracting \
|
||||
duration from time");
|
||||
}
|
||||
SystemTime {
|
||||
t: libc::timeval {
|
||||
tv_sec: secs as libc::time_t,
|
||||
tv_usec: usec as libc::suseconds_t,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SystemTime {
|
||||
fn eq(&self, other: &SystemTime) -> bool {
|
||||
self.t.tv_sec == other.t.tv_sec && self.t.tv_usec == other.t.tv_usec
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SystemTime {}
|
||||
|
||||
impl PartialOrd for SystemTime {
|
||||
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for SystemTime {
|
||||
fn cmp(&self, other: &SystemTime) -> Ordering {
|
||||
let me = (self.t.tv_sec, self.t.tv_usec);
|
||||
let other = (other.t.tv_sec, other.t.tv_usec);
|
||||
me.cmp(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SystemTime")
|
||||
.field("tv_sec", &self.t.tv_sec)
|
||||
.field("tv_usec", &self.t.tv_usec)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn dur2intervals(dur: &Duration) -> u64 {
|
||||
let info = info();
|
||||
let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
|
||||
nanos.checked_add(dur.subsec_nanos() as u64)
|
||||
}).expect("overflow converting duration to nanoseconds");
|
||||
mul_div_u64(nanos, info.denom as u64, info.numer as u64)
|
||||
}
|
||||
|
||||
fn info() -> &'static libc::mach_timebase_info {
|
||||
@ -46,72 +199,190 @@ mod inner {
|
||||
&INFO
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "libstd_sys_internals", issue = "0")]
|
||||
impl<'a> Sub for &'a SteadyTime {
|
||||
type Output = Duration;
|
||||
|
||||
fn sub(self, other: &SteadyTime) -> Duration {
|
||||
let info = info();
|
||||
let diff = self.t as u64 - other.t as u64;
|
||||
let nanos = diff * info.numer as u64 / info.denom as u64;
|
||||
Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
mod inner {
|
||||
use cmp::Ordering;
|
||||
use fmt;
|
||||
use libc;
|
||||
use time::Duration;
|
||||
use ops::Sub;
|
||||
use super::NSEC_PER_SEC;
|
||||
use sys::cvt;
|
||||
use time::Duration;
|
||||
|
||||
pub struct SteadyTime {
|
||||
#[derive(Copy, Clone)]
|
||||
struct Timespec {
|
||||
t: libc::timespec,
|
||||
}
|
||||
|
||||
// Apparently android provides this in some other library?
|
||||
// Bitrig's RT extensions are in the C library, not a separate librt
|
||||
// OpenBSD and NaCl provide it via libc
|
||||
#[cfg(not(any(target_os = "android",
|
||||
target_os = "bitrig",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_env = "musl",
|
||||
target_os = "nacl")))]
|
||||
#[link(name = "rt")]
|
||||
extern {}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Instant {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
impl SteadyTime {
|
||||
pub fn now() -> SteadyTime {
|
||||
let mut t = SteadyTime {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SystemTime {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime {
|
||||
t: Timespec {
|
||||
t: libc::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) }
|
||||
}
|
||||
|
||||
pub fn sub_instant(&self, other: &Instant) -> Duration {
|
||||
self.t.sub_timespec(&other.t).unwrap_or_else(|_| {
|
||||
panic!("other was less than the current instant")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> Instant {
|
||||
Instant { t: self.t.add_duration(other) }
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> Instant {
|
||||
Instant { t: self.t.sub_duration(other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Instant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Instant")
|
||||
.field("tv_sec", &self.t.t.tv_sec)
|
||||
.field("tv_nsec", &self.t.t.tv_nsec)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub fn now() -> SystemTime {
|
||||
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime)
|
||||
-> Result<Duration, Duration> {
|
||||
self.t.sub_timespec(&other.t)
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> SystemTime {
|
||||
SystemTime { t: self.t.add_duration(other) }
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
|
||||
SystemTime { t: self.t.sub_duration(other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SystemTime")
|
||||
.field("tv_sec", &self.t.t.tv_sec)
|
||||
.field("tv_nsec", &self.t.t.tv_nsec)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
pub fn now(clock: libc::c_int) -> Timespec {
|
||||
let mut t = Timespec {
|
||||
t: libc::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC,
|
||||
&mut t.t));
|
||||
}
|
||||
cvt(unsafe {
|
||||
libc::clock_gettime(clock, &mut t.t)
|
||||
}).unwrap();
|
||||
t
|
||||
}
|
||||
|
||||
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
||||
if self >= other {
|
||||
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
|
||||
Duration::new((self.t.tv_sec - other.t.tv_sec) as u64,
|
||||
(self.t.tv_nsec - other.t.tv_nsec) as u32)
|
||||
} else {
|
||||
Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64,
|
||||
self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) -
|
||||
other.t.tv_nsec as u32)
|
||||
})
|
||||
} else {
|
||||
match other.sub_timespec(self) {
|
||||
Ok(d) => Err(d),
|
||||
Err(d) => Ok(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_duration(&self, other: &Duration) -> Timespec {
|
||||
let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
|
||||
let mut secs = secs.expect("overflow when adding duration to time");
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
// in a u32.
|
||||
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
|
||||
if nsec > NSEC_PER_SEC as u32 {
|
||||
nsec -= NSEC_PER_SEC as u32;
|
||||
secs = secs.checked_add(1).expect("overflow when adding \
|
||||
duration to time");
|
||||
}
|
||||
Timespec {
|
||||
t: libc::timespec {
|
||||
tv_sec: secs as libc::time_t,
|
||||
tv_nsec: nsec as libc::c_long,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn sub_duration(&self, other: &Duration) -> Timespec {
|
||||
let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
|
||||
let mut secs = secs.expect("overflow when subtracting duration \
|
||||
from time");
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
|
||||
if nsec < 0 {
|
||||
nsec += NSEC_PER_SEC as i32;
|
||||
secs = secs.checked_sub(1).expect("overflow when subtracting \
|
||||
duration from time");
|
||||
}
|
||||
Timespec {
|
||||
t: libc::timespec {
|
||||
tv_sec: secs as libc::time_t,
|
||||
tv_nsec: nsec as libc::c_long,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "libstd_sys_internals", issue = "0")]
|
||||
impl<'a> Sub for &'a SteadyTime {
|
||||
type Output = Duration;
|
||||
impl PartialEq for Timespec {
|
||||
fn eq(&self, other: &Timespec) -> bool {
|
||||
self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
|
||||
}
|
||||
}
|
||||
|
||||
fn sub(self, other: &SteadyTime) -> Duration {
|
||||
if self.t.tv_nsec >= other.t.tv_nsec {
|
||||
Duration::new(self.t.tv_sec as u64 - other.t.tv_sec as u64,
|
||||
self.t.tv_nsec as u32 - other.t.tv_nsec as u32)
|
||||
} else {
|
||||
Duration::new(self.t.tv_sec as u64 - 1 - other.t.tv_sec as u64,
|
||||
self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) -
|
||||
other.t.tv_nsec as u32)
|
||||
}
|
||||
impl Eq for Timespec {}
|
||||
|
||||
impl PartialOrd for Timespec {
|
||||
fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Timespec {
|
||||
fn cmp(&self, other: &Timespec) -> Ordering {
|
||||
let me = (self.t.tv_sec, self.t.tv_nsec);
|
||||
let other = (other.t.tv_sec, other.t.tv_nsec);
|
||||
me.cmp(&other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ pub type LPWSANETWORKEVENTS = *mut WSANETWORKEVENTS;
|
||||
pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN;
|
||||
pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
|
||||
pub type LPWSTR = *mut WCHAR;
|
||||
pub type LPFILETIME = *mut FILETIME;
|
||||
|
||||
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
|
||||
pub type PLARGE_INTEGER = *mut c_longlong;
|
||||
@ -1231,6 +1232,7 @@ extern "system" {
|
||||
ReturnValue: LPVOID,
|
||||
OriginalContext: *const CONTEXT,
|
||||
HistoryTable: *const UNWIND_HISTORY_TABLE);
|
||||
pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
|
||||
}
|
||||
|
||||
// Functions that aren't available on Windows XP, but we still use them and just
|
||||
|
@ -8,23 +8,173 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ops::Sub;
|
||||
use cmp::Ordering;
|
||||
use fmt;
|
||||
use mem;
|
||||
use sync::Once;
|
||||
use sys::c;
|
||||
use sys::cvt;
|
||||
use sys_common::mul_div_u64;
|
||||
use time::Duration;
|
||||
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
|
||||
|
||||
pub struct SteadyTime {
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
||||
pub struct Instant {
|
||||
t: c::LARGE_INTEGER,
|
||||
}
|
||||
|
||||
impl SteadyTime {
|
||||
pub fn now() -> SteadyTime {
|
||||
let mut t = SteadyTime { t: 0 };
|
||||
unsafe { c::QueryPerformanceCounter(&mut t.t); }
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SystemTime {
|
||||
t: c::FILETIME,
|
||||
}
|
||||
|
||||
const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32,
|
||||
dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32,
|
||||
},
|
||||
};
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
let mut t = Instant { t: 0 };
|
||||
cvt(unsafe {
|
||||
c::QueryPerformanceCounter(&mut t.t)
|
||||
}).unwrap();
|
||||
t
|
||||
}
|
||||
|
||||
pub fn sub_instant(&self, other: &Instant) -> Duration {
|
||||
// Values which are +- 1 need to be considered as basically the same
|
||||
// units in time due to various measurement oddities, according to
|
||||
// Windows [1]
|
||||
//
|
||||
// [1]:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop
|
||||
// /dn553408%28v=vs.85%29.aspx#guidance
|
||||
if other.t > self.t && other.t - self.t == 1 {
|
||||
return Duration::new(0, 0)
|
||||
}
|
||||
let diff = (self.t as u64).checked_sub(other.t as u64)
|
||||
.expect("specified instant was later than \
|
||||
self");
|
||||
let nanos = mul_div_u64(diff, NANOS_PER_SEC, frequency() as u64);
|
||||
Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32)
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> Instant {
|
||||
let freq = frequency() as u64;
|
||||
let t = other.as_secs().checked_mul(freq).and_then(|i| {
|
||||
(self.t as u64).checked_add(i)
|
||||
}).and_then(|i| {
|
||||
i.checked_add(mul_div_u64(other.subsec_nanos() as u64, freq,
|
||||
NANOS_PER_SEC))
|
||||
}).expect("overflow when adding duration to time");
|
||||
Instant {
|
||||
t: t as c::LARGE_INTEGER,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> Instant {
|
||||
let freq = frequency() as u64;
|
||||
let t = other.as_secs().checked_mul(freq).and_then(|i| {
|
||||
(self.t as u64).checked_sub(i)
|
||||
}).and_then(|i| {
|
||||
i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq,
|
||||
NANOS_PER_SEC))
|
||||
}).expect("overflow when subtracting duration from time");
|
||||
Instant {
|
||||
t: t as c::LARGE_INTEGER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub fn now() -> SystemTime {
|
||||
unsafe {
|
||||
let mut t: SystemTime = mem::zeroed();
|
||||
c::GetSystemTimeAsFileTime(&mut t.t);
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
fn from_intervals(intervals: i64) -> SystemTime {
|
||||
SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: intervals as c::DWORD,
|
||||
dwHighDateTime: (intervals >> 32) as c::DWORD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn intervals(&self) -> i64 {
|
||||
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
let me = self.intervals();
|
||||
let other = other.intervals();
|
||||
if me >= other {
|
||||
Ok(intervals2dur((me - other) as u64))
|
||||
} else {
|
||||
Err(intervals2dur((other - me) as u64))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_duration(&self, other: &Duration) -> SystemTime {
|
||||
let intervals = self.intervals().checked_add(dur2intervals(other))
|
||||
.expect("overflow when adding duration to time");
|
||||
SystemTime::from_intervals(intervals)
|
||||
}
|
||||
|
||||
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
|
||||
let intervals = self.intervals().checked_sub(dur2intervals(other))
|
||||
.expect("overflow when subtracting from time");
|
||||
SystemTime::from_intervals(intervals)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SystemTime {
|
||||
fn eq(&self, other: &SystemTime) -> bool {
|
||||
self.intervals() == other.intervals()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SystemTime {}
|
||||
|
||||
impl PartialOrd for SystemTime {
|
||||
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for SystemTime {
|
||||
fn cmp(&self, other: &SystemTime) -> Ordering {
|
||||
self.intervals().cmp(&other.intervals())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SystemTime")
|
||||
.field("intervals", &self.intervals())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn dur2intervals(d: &Duration) -> i64 {
|
||||
d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| {
|
||||
i.checked_add(d.subsec_nanos() as u64 / 100)
|
||||
}).expect("overflow when converting duration to intervals") as i64
|
||||
}
|
||||
|
||||
fn intervals2dur(intervals: u64) -> Duration {
|
||||
Duration::new(intervals / INTERVALS_PER_SEC,
|
||||
((intervals % INTERVALS_PER_SEC) * 100) as u32)
|
||||
}
|
||||
|
||||
fn frequency() -> c::LARGE_INTEGER {
|
||||
@ -33,37 +183,8 @@ fn frequency() -> c::LARGE_INTEGER {
|
||||
|
||||
unsafe {
|
||||
ONCE.call_once(|| {
|
||||
c::QueryPerformanceFrequency(&mut FREQUENCY);
|
||||
cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap();
|
||||
});
|
||||
FREQUENCY
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "libstd_sys_internals", issue = "0")]
|
||||
impl<'a> Sub for &'a SteadyTime {
|
||||
type Output = Duration;
|
||||
|
||||
fn sub(self, other: &SteadyTime) -> Duration {
|
||||
let diff = self.t as u64 - other.t as u64;
|
||||
let nanos = mul_div_u64(diff, NANOS_PER_SEC, frequency() as u64);
|
||||
Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
// 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_u64(value: u64, numer: u64, denom: u64) -> u64 {
|
||||
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_u64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
|
||||
1_000_000_000_001_000);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use ops::{Add, Sub, Mul, Div};
|
||||
use sys::time::SteadyTime;
|
||||
use time::Instant;
|
||||
|
||||
const NANOS_PER_SEC: u32 = 1_000_000_000;
|
||||
const NANOS_PER_MILLI: u32 = 1_000_000;
|
||||
@ -67,9 +67,9 @@ impl Duration {
|
||||
abstraction",
|
||||
issue = "27799")]
|
||||
pub fn span<F>(f: F) -> Duration where F: FnOnce() {
|
||||
let start = SteadyTime::now();
|
||||
let start = Instant::now();
|
||||
f();
|
||||
&SteadyTime::now() - &start
|
||||
start.elapsed()
|
||||
}
|
||||
|
||||
/// Creates a new `Duration` from the specified number of seconds.
|
||||
|
@ -12,7 +12,311 @@
|
||||
|
||||
#![stable(feature = "time", since = "1.3.0")]
|
||||
|
||||
use error::Error;
|
||||
use fmt;
|
||||
use ops::{Add, Sub};
|
||||
use sys::time;
|
||||
|
||||
#[stable(feature = "time", since = "1.3.0")]
|
||||
pub use self::duration::Duration;
|
||||
|
||||
mod duration;
|
||||
|
||||
/// A measurement of a monotonically increasing clock which is suitable for
|
||||
/// measuring the amount of time that an operation takes.
|
||||
///
|
||||
/// Instants are guaranteed always be greater than any previously measured
|
||||
/// instant when created, and are often useful for tasks such as measuring
|
||||
/// benchmarks or timing how long an operation takes.
|
||||
///
|
||||
/// Note, however, that instants are not guaranteed to be **steady**. In other
|
||||
/// words each tick of the underlying clock may not be the same length (e.g.
|
||||
/// some seconds may be longer than others). An instant may jump forwards or
|
||||
/// experience time dilation (slow down or speed up), but it will never go
|
||||
/// backwards.
|
||||
///
|
||||
/// Instants are opaque types that can only be compared to one another. There is
|
||||
/// no method to get "the number of seconds" from an instant but instead it only
|
||||
/// allow learning the duration between two instants (or comparing two
|
||||
/// instants).
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
pub struct Instant(time::Instant);
|
||||
|
||||
/// A measurement of the system clock appropriate for timestamps such as those
|
||||
/// on files on the filesystem.
|
||||
///
|
||||
/// Distinct from the `Instant` type, this time measurement **is not
|
||||
/// monotonic**. This means that you can save a file to the file system, then
|
||||
/// save another file to the file system, **and the second file has a
|
||||
/// `SystemTime` measurement earlier than the second**. In other words, an
|
||||
/// operation that happens after another operation in real time may have an
|
||||
/// earlier `SystemTime`!
|
||||
///
|
||||
/// Consequently, comparing two `SystemTime` instances to learn about the
|
||||
/// duration between them returns a `Result` instead of an infallible `Duration`
|
||||
/// to indicate that this sort of time drift may happen and needs to be handled.
|
||||
///
|
||||
/// Although a `SystemTime` cannot be directly inspected, the `UNIX_EPOCH`
|
||||
/// constant is provided in this module as an anchor in time to learn
|
||||
/// information about a `SystemTime`. By calculating the duration from this
|
||||
/// fixed point in time a `SystemTime` can be converted to a human-readable time
|
||||
/// or perhaps some other string representation.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
pub struct SystemTime(time::SystemTime);
|
||||
|
||||
/// An error returned from the `duration_from_earlier` method on `SystemTime`,
|
||||
/// used to learn about why how far in the opposite direction a timestamp lies.
|
||||
#[derive(Clone, Debug)]
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
pub struct SystemTimeError(Duration);
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Instant {
|
||||
/// Returns an instant corresponding to "now".
|
||||
pub fn now() -> Instant {
|
||||
Instant(time::Instant::now())
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed from another instant to this one.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `earlier` is later than `self`, which should
|
||||
/// only be possible if `earlier` was created after `self`. Because
|
||||
/// `Instant` is monotonic, the only time that this should happen should be
|
||||
/// a bug.
|
||||
pub fn duration_from_earlier(&self, earlier: Instant) -> Duration {
|
||||
self.0.sub_instant(&earlier.0)
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed since this instant was created.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if the current time is earlier than this instant
|
||||
/// which can happen if an `Instant` is produced synthetically.
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
Instant::now().duration_from_earlier(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Add<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
fn add(self, other: Duration) -> Instant {
|
||||
Instant(self.0.add_duration(&other))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Sub<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
fn sub(self, other: Duration) -> Instant {
|
||||
Instant(self.0.sub_duration(&other))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl fmt::Debug for Instant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl SystemTime {
|
||||
/// Returns the system time corresponding to "now".
|
||||
pub fn now() -> SystemTime {
|
||||
SystemTime(time::SystemTime::now())
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed from an earlier point in time.
|
||||
///
|
||||
/// This function may fail because measurements taken earlier are not
|
||||
/// guaranteed to always be before later measurements (due to anomalies such
|
||||
/// as the system clock being adjusted either forwards or backwards).
|
||||
///
|
||||
/// If successful, `Ok(duration)` is returned where the duration represents
|
||||
/// the amount of time elapsed from the specified measurement to this one.
|
||||
///
|
||||
/// Returns an `Err` if `earlier` is later than `self`, and the error
|
||||
/// contains how far from `self` the time is.
|
||||
pub fn duration_from_earlier(&self, earlier: SystemTime)
|
||||
-> Result<Duration, SystemTimeError> {
|
||||
self.0.sub_time(&earlier.0).map_err(SystemTimeError)
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed since this system time was created.
|
||||
///
|
||||
/// This function may fail as the underlying system clock is susceptible to
|
||||
/// drift and updates (e.g. the system clock could go backwards), so this
|
||||
/// function may not always succeed. If successful, `Ok(duration)` is
|
||||
/// returned where the duration represents the amount of time elapsed from
|
||||
/// this time measurement to the current time.
|
||||
///
|
||||
/// Returns an `Err` if `self` is later than the current system time, and
|
||||
/// the error contains how far from the current system time `self` is.
|
||||
pub fn elapsed(&self) -> Result<Duration, SystemTimeError> {
|
||||
SystemTime::now().duration_from_earlier(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Add<Duration> for SystemTime {
|
||||
type Output = SystemTime;
|
||||
|
||||
fn add(self, dur: Duration) -> SystemTime {
|
||||
SystemTime(self.0.add_duration(&dur))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Sub<Duration> for SystemTime {
|
||||
type Output = SystemTime;
|
||||
|
||||
fn sub(self, dur: Duration) -> SystemTime {
|
||||
SystemTime(self.0.sub_duration(&dur))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An anchor in time which can be used to create new `SystemTime` instances or
|
||||
/// learn about where in time a `SystemTime` lies.
|
||||
///
|
||||
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
|
||||
/// respect to the system clock. Using `duration_from_earlier` on an existing
|
||||
/// `SystemTime` instance can tell how far away from this point in time a
|
||||
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
|
||||
/// `SystemTime` instance to represent another fixed point in time.
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH);
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl SystemTimeError {
|
||||
/// Returns the positive duration which represents how far forward the
|
||||
/// second system time was from the first.
|
||||
///
|
||||
/// A `SystemTimeError` is returned from the `duration_from_earlier`
|
||||
/// operation whenever the second duration, `earlier`, actually represents a
|
||||
/// point later in time than the `self` of the method call. This function
|
||||
/// will extract and return the amount of time later `earlier` actually is.
|
||||
pub fn duration(&self) -> Duration {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl Error for SystemTimeError {
|
||||
fn description(&self) -> &str { "other time was not earlier than self" }
|
||||
}
|
||||
|
||||
#[unstable(feature = "time2", reason = "recently added", issue = "29866")]
|
||||
impl fmt::Display for SystemTimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "second time provided was later than self")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Instant, SystemTime, Duration, UNIX_EPOCH};
|
||||
|
||||
#[test]
|
||||
fn instant_monotonic() {
|
||||
let a = Instant::now();
|
||||
let b = Instant::now();
|
||||
assert!(b >= a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instant_elapsed() {
|
||||
let a = Instant::now();
|
||||
a.elapsed();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instant_math() {
|
||||
let a = Instant::now();
|
||||
let b = Instant::now();
|
||||
let dur = b.duration_from_earlier(a);
|
||||
assert_eq!(b - dur, a);
|
||||
assert_eq!(a + dur, b);
|
||||
|
||||
let second = Duration::new(1, 0);
|
||||
assert_eq!(a - second + second, a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn instant_duration_panic() {
|
||||
let a = Instant::now();
|
||||
(a - Duration::new(1, 0)).duration_from_earlier(a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_time_math() {
|
||||
let a = SystemTime::now();
|
||||
let b = SystemTime::now();
|
||||
match b.duration_from_earlier(a) {
|
||||
Ok(dur) if dur == Duration::new(0, 0) => {
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
Ok(dur) => {
|
||||
assert!(b > a);
|
||||
assert_eq!(b - dur, a);
|
||||
assert_eq!(a + dur, b);
|
||||
}
|
||||
Err(dur) => {
|
||||
let dur = dur.duration();
|
||||
assert!(a > b);
|
||||
assert_eq!(b + dur, a);
|
||||
assert_eq!(b - dur, a);
|
||||
}
|
||||
}
|
||||
|
||||
let second = Duration::new(1, 0);
|
||||
assert_eq!(a.duration_from_earlier(a - second).unwrap(), second);
|
||||
assert_eq!(a.duration_from_earlier(a + second).unwrap_err().duration(),
|
||||
second);
|
||||
|
||||
assert_eq!(a - second + second, a);
|
||||
|
||||
let eighty_years = second * 60 * 60 * 24 * 365 * 80;
|
||||
assert_eq!(a - eighty_years + eighty_years, a);
|
||||
assert_eq!(a - (eighty_years * 10) + (eighty_years * 10), a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_time_elapsed() {
|
||||
let a = SystemTime::now();
|
||||
drop(a.elapsed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn since_epoch() {
|
||||
let ts = SystemTime::now();
|
||||
let a = ts.duration_from_earlier(UNIX_EPOCH).unwrap();
|
||||
let b = ts.duration_from_earlier(UNIX_EPOCH - Duration::new(1, 0)).unwrap();
|
||||
assert!(b > a);
|
||||
assert_eq!(b - a, Duration::new(1, 0));
|
||||
|
||||
// let's assume that we're all running computers later than 2000
|
||||
let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30;
|
||||
assert!(a > thirty_years);
|
||||
|
||||
// let's assume that we're all running computers earlier than 2090.
|
||||
// Should give us ~70 years to fix this!
|
||||
let hundred_twenty_years = thirty_years * 4;
|
||||
assert!(a < hundred_twenty_years);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user