std: Add Instant and SystemTime to std::time

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.
This commit is contained in:
Alex Crichton 2015-11-16 17:36:14 -08:00
parent 22e31f10c2
commit c6eb8527e0
8 changed files with 816 additions and 102 deletions

View File

@ -12,11 +12,10 @@ use prelude::v1::*;
use sync::atomic::{AtomicUsize, Ordering}; use sync::atomic::{AtomicUsize, Ordering};
use sync::{mutex, MutexGuard, PoisonError}; use sync::{mutex, MutexGuard, PoisonError};
use sys::time::SteadyTime;
use sys_common::condvar as sys; use sys_common::condvar as sys;
use sys_common::mutex as sys_mutex; use sys_common::mutex as sys_mutex;
use sys_common::poison::{self, LockResult}; 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 /// A type indicating whether a timed wait on a condition variable returned
/// due to a time out or not. /// due to a time out or not.
@ -345,14 +344,13 @@ impl StaticCondvar {
where F: FnMut(LockResult<&mut T>) -> bool { where F: FnMut(LockResult<&mut T>) -> bool {
// This could be made more efficient by pushing the implementation into // This could be made more efficient by pushing the implementation into
// sys::condvar // sys::condvar
let start = SteadyTime::now(); let start = Instant::now();
let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard); let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard);
while !f(guard_result while !f(guard_result
.as_mut() .as_mut()
.map(|g| &mut **g) .map(|g| &mut **g)
.map_err(|e| PoisonError::new(&mut **e.get_mut()))) { .map_err(|e| PoisonError::new(&mut **e.get_mut()))) {
let now = SteadyTime::now(); let consumed = start.elapsed();
let consumed = &now - &start;
let guard = guard_result.unwrap_or_else(|e| e.into_inner()); let guard = guard_result.unwrap_or_else(|e| e.into_inner());
let (new_guard_result, timed_out) = if consumed > dur { let (new_guard_result, timed_out) = if consumed > dur {
(Ok(guard), WaitTimeoutResult(true)) (Ok(guard), WaitTimeoutResult(true))

View File

@ -98,3 +98,22 @@ pub fn cleanup() {
at_exit_imp::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);
}

View File

@ -12,8 +12,7 @@ use cell::UnsafeCell;
use libc; use libc;
use ptr; use ptr;
use sys::mutex::{self, Mutex}; use sys::mutex::{self, Mutex};
use sys::time; use time::{Instant, Duration};
use time::Duration;
pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> } 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 // stable time. pthread_cond_timedwait uses system time, but we want to
// report timeout based on stable time. // report timeout based on stable time.
let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 }; 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()); let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
debug_assert_eq!(r, 0); debug_assert_eq!(r, 0);
@ -81,7 +80,7 @@ impl Condvar {
// ETIMEDOUT is not a totally reliable method of determining timeout due // ETIMEDOUT is not a totally reliable method of determining timeout due
// to clock shifts, so do the check ourselves // to clock shifts, so do the check ourselves
&time::SteadyTime::now() - &stable_now < dur stable_now.elapsed() < dur
} }
#[inline] #[inline]

View File

@ -8,28 +8,181 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // 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; const NSEC_PER_SEC: u64 = 1_000_000_000;
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
mod inner { mod inner {
use cmp::Ordering;
use fmt;
use libc; use libc;
use time::Duration;
use ops::Sub;
use sync::Once;
use super::NSEC_PER_SEC; 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 t: u64
} }
impl SteadyTime { #[derive(Copy, Clone)]
pub fn now() -> SteadyTime { pub struct SystemTime {
SteadyTime { t: libc::timeval,
t: unsafe { libc::mach_absolute_time() }, }
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 { fn info() -> &'static libc::mach_timebase_info {
@ -46,72 +199,190 @@ mod inner {
&INFO &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")))] #[cfg(not(any(target_os = "macos", target_os = "ios")))]
mod inner { mod inner {
use cmp::Ordering;
use fmt;
use libc; use libc;
use time::Duration;
use ops::Sub;
use super::NSEC_PER_SEC; use super::NSEC_PER_SEC;
use sys::cvt;
use time::Duration;
pub struct SteadyTime { #[derive(Copy, Clone)]
struct Timespec {
t: libc::timespec, t: libc::timespec,
} }
// Apparently android provides this in some other library? #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
// Bitrig's RT extensions are in the C library, not a separate librt pub struct Instant {
// OpenBSD and NaCl provide it via libc t: Timespec,
#[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 {}
impl SteadyTime { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub fn now() -> SteadyTime { pub struct SystemTime {
let mut t = SteadyTime { 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 { t: libc::timespec {
tv_sec: 0, tv_sec: 0,
tv_nsec: 0, tv_nsec: 0,
} }
}; };
unsafe { cvt(unsafe {
assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC, libc::clock_gettime(clock, &mut t.t)
&mut t.t)); }).unwrap();
}
t 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 PartialEq for Timespec {
impl<'a> Sub for &'a SteadyTime { fn eq(&self, other: &Timespec) -> bool {
type Output = Duration; self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
}
}
fn sub(self, other: &SteadyTime) -> Duration { impl Eq for Timespec {}
if self.t.tv_nsec >= other.t.tv_nsec {
Duration::new(self.t.tv_sec as u64 - other.t.tv_sec as u64, impl PartialOrd for Timespec {
self.t.tv_nsec as u32 - other.t.tv_nsec as u32) fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
} else { Some(self.cmp(other))
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 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)
} }
} }
} }

View File

@ -65,6 +65,7 @@ pub type LPWSANETWORKEVENTS = *mut WSANETWORKEVENTS;
pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN; pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN;
pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
pub type LPWSTR = *mut WCHAR; pub type LPWSTR = *mut WCHAR;
pub type LPFILETIME = *mut FILETIME;
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
pub type PLARGE_INTEGER = *mut c_longlong; pub type PLARGE_INTEGER = *mut c_longlong;
@ -1231,6 +1232,7 @@ extern "system" {
ReturnValue: LPVOID, ReturnValue: LPVOID,
OriginalContext: *const CONTEXT, OriginalContext: *const CONTEXT,
HistoryTable: *const UNWIND_HISTORY_TABLE); 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 // Functions that aren't available on Windows XP, but we still use them and just

View File

@ -8,23 +8,173 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use ops::Sub; use cmp::Ordering;
use fmt;
use mem;
use sync::Once; use sync::Once;
use sys::c; use sys::c;
use sys::cvt;
use sys_common::mul_div_u64;
use time::Duration; use time::Duration;
const NANOS_PER_SEC: u64 = 1_000_000_000; 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, t: c::LARGE_INTEGER,
} }
impl SteadyTime { #[derive(Copy, Clone)]
pub fn now() -> SteadyTime { pub struct SystemTime {
let mut t = SteadyTime { t: 0 }; t: c::FILETIME,
unsafe { c::QueryPerformanceCounter(&mut t.t); } }
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 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 { fn frequency() -> c::LARGE_INTEGER {
@ -33,37 +183,8 @@ fn frequency() -> c::LARGE_INTEGER {
unsafe { unsafe {
ONCE.call_once(|| { ONCE.call_once(|| {
c::QueryPerformanceFrequency(&mut FREQUENCY); cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap();
}); });
FREQUENCY 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);
}

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use ops::{Add, Sub, Mul, Div}; 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_SEC: u32 = 1_000_000_000;
const NANOS_PER_MILLI: u32 = 1_000_000; const NANOS_PER_MILLI: u32 = 1_000_000;
@ -67,9 +67,9 @@ impl Duration {
abstraction", abstraction",
issue = "27799")] issue = "27799")]
pub fn span<F>(f: F) -> Duration where F: FnOnce() { pub fn span<F>(f: F) -> Duration where F: FnOnce() {
let start = SteadyTime::now(); let start = Instant::now();
f(); f();
&SteadyTime::now() - &start start.elapsed()
} }
/// Creates a new `Duration` from the specified number of seconds. /// Creates a new `Duration` from the specified number of seconds.

View File

@ -12,7 +12,311 @@
#![stable(feature = "time", since = "1.3.0")] #![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")] #[stable(feature = "time", since = "1.3.0")]
pub use self::duration::Duration; pub use self::duration::Duration;
mod 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);
}
}