From c0d3aa83db3727debf1ce20210fd170ad9c68c99 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 28 Apr 2015 11:40:04 -0700 Subject: [PATCH] std: Redesign Duration, implementing RFC 1040 This commit is an implementation of [RFC 1040][rfc] which is a redesign of the currently-unstable `Duration` type. The API of the type has been scaled back to be more conservative and it also no longer supports negative durations. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1040-duration-reform.md The inner `duration` module of the `time` module has now been hidden (as `Duration` is reexported) and the feature name for this type has changed from `std_misc` to `duration`. All APIs accepting durations have also been audited to take a more flavorful feature name instead of `std_misc`. Closes #24874 --- src/librustc/lib.rs | 1 + src/librustc/metadata/loader.rs | 3 +- src/librustc/util/common.rs | 4 +- src/libstd/sync/condvar.rs | 69 +- src/libstd/sys/unix/condvar.rs | 22 +- src/libstd/sys/unix/thread.rs | 9 +- src/libstd/sys/unix/time.rs | 26 +- src/libstd/sys/windows/condvar.rs | 2 +- src/libstd/sys/windows/mod.rs | 20 + src/libstd/sys/windows/thread.rs | 10 +- src/libstd/sys/windows/time.rs | 20 +- src/libstd/thread/mod.rs | 30 +- src/libstd/time/duration.rs | 662 +++++------------- src/libstd/time/mod.rs | 13 +- src/libtest/lib.rs | 11 +- src/test/bench/core-map.rs | 2 +- src/test/bench/core-set.rs | 14 +- src/test/bench/core-std.rs | 4 +- src/test/bench/msgsend-pipes-shared.rs | 8 +- src/test/bench/msgsend-pipes.rs | 8 +- src/test/bench/msgsend-ring-mutex-arcs.rs | 10 +- src/test/bench/shootout-pfib.rs | 2 +- src/test/bench/std-smallintmap.rs | 10 +- src/test/bench/task-perf-alloc-unwind.rs | 2 +- src/test/run-pass/core-run-destroy.rs | 2 +- .../run-pass/std-sync-right-kind-impls.rs | 2 +- 26 files changed, 347 insertions(+), 619 deletions(-) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index d4012f2057b5d..587afca9cf05f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -40,6 +40,7 @@ #![feature(path_ext)] #![feature(str_char)] #![feature(into_cow)] +#![feature(duration)] #![feature(slice_patterns)] #![cfg_attr(test, feature(test))] diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index bbb2452ca29ee..bc7f651413c2e 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -720,8 +720,7 @@ fn get_metadata_section(is_osx: bool, filename: &Path) -> Result {}ms", filename.file_name().unwrap(), - dur.num_milliseconds()); + info!("reading {:?} => {}", filename.file_name().unwrap(), dur); return ret.unwrap();; } diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index d71a68e205052..5a5567c48adee 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -55,8 +55,8 @@ pub fn time(do_it: bool, what: &str, u: U, f: F) -> T where }; let rv = rv.unwrap(); - println!("{}time: {}.{:03} \t{}", repeat(" ").take(old).collect::(), - dur.num_seconds(), dur.num_milliseconds() % 1000, what); + println!("{}time: {} \t{}", repeat(" ").take(old).collect::(), + dur, what); DEPTH.with(|slot| slot.set(old)); rv diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index c2964b7a4f125..963f5da4dc6f5 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -69,12 +69,12 @@ pub struct Condvar { inner: Box } /// # Examples /// /// ``` -/// # #![feature(std_misc)] +/// # #![feature(static_condvar)] /// use std::sync::{StaticCondvar, CONDVAR_INIT}; /// /// static CVAR: StaticCondvar = CONDVAR_INIT; /// ``` -#[unstable(feature = "std_misc", +#[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub struct StaticCondvar { inner: sys::Condvar, @@ -82,7 +82,7 @@ pub struct StaticCondvar { } /// Constant initializer for a statically allocated condition variable. -#[unstable(feature = "std_misc", +#[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub const CONDVAR_INIT: StaticCondvar = StaticCondvar { inner: sys::CONDVAR_INIT, @@ -161,6 +161,30 @@ impl Condvar { } } + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to `wait()` except that + /// the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// The returned boolean is `false` only if the timeout is known + /// to have elapsed. + /// + /// Like `wait`, the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + #[unstable(feature = "wait_timeout", reason = "waiting for Duration")] + pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, + dur: Duration) + -> LockResult<(MutexGuard<'a, T>, bool)> { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait_timeout(guard, dur) + } + } + /// Waits on this condition variable for a notification, timing out after a /// specified duration. /// @@ -214,7 +238,7 @@ impl StaticCondvar { /// notification. /// /// See `Condvar::wait`. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub fn wait<'a, T>(&'static self, guard: MutexGuard<'a, T>) -> LockResult> { @@ -235,14 +259,27 @@ impl StaticCondvar { /// specified duration. /// /// See `Condvar::wait_timeout`. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub fn wait_timeout_ms<'a, T>(&'static self, guard: MutexGuard<'a, T>, ms: u32) -> LockResult<(MutexGuard<'a, T>, bool)> { + self.wait_timeout(guard, Duration::from_millis(ms as u64)) + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// See `Condvar::wait_timeout`. + #[unstable(feature = "static_condvar", + reason = "may be merged with Condvar in the future")] + pub fn wait_timeout<'a, T>(&'static self, + guard: MutexGuard<'a, T>, + timeout: Duration) + -> LockResult<(MutexGuard<'a, T>, bool)> { let (poisoned, success) = unsafe { let lock = mutex::guard_lock(&guard); self.verify(lock); - let success = self.inner.wait_timeout(lock, Duration::milliseconds(ms as i64)); + let success = self.inner.wait_timeout(lock, timeout); (mutex::guard_poison(&guard).get(), success) }; if poisoned { @@ -259,7 +296,7 @@ impl StaticCondvar { /// passed and the function returns `false`. /// /// See `Condvar::wait_timeout_with`. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub fn wait_timeout_with<'a, T, F>(&'static self, guard: MutexGuard<'a, T>, @@ -267,7 +304,8 @@ impl StaticCondvar { mut f: F) -> LockResult<(MutexGuard<'a, T>, bool)> where F: FnMut(LockResult<&mut T>) -> bool { - // This could be made more efficient by pushing the implementation into sys::condvar + // This could be made more efficient by pushing the implementation into + // sys::condvar let start = SteadyTime::now(); let mut guard_result: LockResult> = Ok(guard); while !f(guard_result @@ -277,7 +315,7 @@ impl StaticCondvar { let now = SteadyTime::now(); let consumed = &now - &start; let guard = guard_result.unwrap_or_else(|e| e.into_inner()); - let res = self.wait_timeout_ms(guard, (dur - consumed).num_milliseconds() as u32); + let res = self.wait_timeout(guard, dur - consumed); let (new_guard_result, no_timeout) = match res { Ok((new_guard, no_timeout)) => (Ok(new_guard), no_timeout), Err(err) => { @@ -301,14 +339,14 @@ impl StaticCondvar { /// Wakes up one blocked thread on this condvar. /// /// See `Condvar::notify_one`. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } /// Wakes up all blocked threads on this condvar. /// /// See `Condvar::notify_all`. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } @@ -318,7 +356,7 @@ impl StaticCondvar { /// active users of the condvar, and this also doesn't prevent any future /// users of the condvar. This method is required to be called to not leak /// memory on all platforms. - #[unstable(feature = "std_misc", + #[unstable(feature = "static_condvar", reason = "may be merged with Condvar in the future")] pub unsafe fn destroy(&'static self) { self.inner.destroy() @@ -447,7 +485,9 @@ mod tests { static S: AtomicUsize = ATOMIC_USIZE_INIT; let g = M.lock().unwrap(); - let (g, success) = C.wait_timeout_with(g, Duration::nanoseconds(1000), |_| false).unwrap(); + let (g, success) = C.wait_timeout_with(g, Duration::new(0, 1000), |_| { + false + }).unwrap(); assert!(!success); let (tx, rx) = channel(); @@ -471,7 +511,8 @@ mod tests { }); let mut state = 0; - let (_g, success) = C.wait_timeout_with(g, Duration::days(1), |_| { + let day = 24 * 60 * 60; + let (_g, success) = C.wait_timeout_with(g, Duration::new(day, 0), |_| { assert_eq!(state, S.load(Ordering::SeqCst)); tx.send(()).unwrap(); state += 1; diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index ed6382e000ac9..34bb9b57e7fc2 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -57,25 +57,20 @@ impl Condvar { // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - if dur <= Duration::zero() { - return false; - } - - // First, figure out what time it currently is, in both system and stable time. - // pthread_cond_timedwait uses system time, but we want to report timeout based on stable - // time. + // First, figure out what time it currently is, in both system and + // 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 r = ffi::gettimeofday(&mut sys_now, ptr::null_mut()); debug_assert_eq!(r, 0); - let seconds = dur.num_seconds() as libc::time_t; + let seconds = dur.secs() as libc::time_t; let timeout = match sys_now.tv_sec.checked_add(seconds) { Some(sec) => { libc::timespec { tv_sec: sec, - tv_nsec: (dur - Duration::seconds(dur.num_seconds())) - .num_nanoseconds().unwrap() as libc::c_long, + tv_nsec: dur.nanos() as libc::c_long, } } None => { @@ -87,11 +82,12 @@ impl Condvar { }; // And wait! - let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); + let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), + &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); - // ETIMEDOUT is not a totally reliable method of determining timeout due to clock shifts, - // so do the check ourselves + // 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 } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index cfab9d1c51a15..e1f4966c99dd8 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -129,14 +129,9 @@ impl Thread { } pub fn sleep(dur: Duration) { - if dur < Duration::zero() { - return Thread::yield_now() - } - let seconds = dur.num_seconds(); - let ns = dur - Duration::seconds(seconds); let mut ts = libc::timespec { - tv_sec: seconds as libc::time_t, - tv_nsec: ns.num_nanoseconds().unwrap() as libc::c_long, + tv_sec: dur.secs() as libc::time_t, + tv_nsec: dur.nanos() as libc::c_long, }; // If we're awoken with a signal then the return value will be -1 and diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index f59eb2c03013f..fcbc723c39ac5 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -32,11 +32,6 @@ mod inner { t: unsafe { mach_absolute_time() }, } } - - pub fn ns(&self) -> u64 { - let info = info(); - self.t * info.numer as u64 / info.denom as u64 - } } fn info() -> &'static libc::mach_timebase_info { @@ -59,8 +54,9 @@ mod inner { fn sub(self, other: &SteadyTime) -> Duration { let info = info(); - let diff = self.t as i64 - other.t as i64; - Duration::nanoseconds(diff * info.numer as i64 / info.denom as i64) + let diff = self.t as u64 - other.t as u64; + let nanos = diff * info.numer as u64 / info.denom as u64; + Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) } } } @@ -71,7 +67,7 @@ mod inner { use time::Duration; use ops::Sub; - const NSEC_PER_SEC: i64 = 1_000_000_000; + const NSEC_PER_SEC: u32 = 1_000_000_000; pub struct SteadyTime { t: libc::timespec, @@ -104,10 +100,6 @@ mod inner { } t } - - pub fn ns(&self) -> u64 { - self.t.tv_sec as u64 * NSEC_PER_SEC as u64 + self.t.tv_nsec as u64 - } } impl<'a> Sub for &'a SteadyTime { @@ -115,12 +107,12 @@ mod inner { fn sub(self, other: &SteadyTime) -> Duration { if self.t.tv_nsec >= other.t.tv_nsec { - Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + - Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) + 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::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + - Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC - - other.t.tv_nsec as i64) + Duration::new(self.t.tv_sec as u64 - 1 - other.t.tv_sec as u64, + self.t.tv_nsec as u32 + NSEC_PER_SEC - + other.t.tv_nsec as u32) } } } diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs index 67552255fdbeb..8bb2326e4d6b9 100644 --- a/src/libstd/sys/windows/condvar.rs +++ b/src/libstd/sys/windows/condvar.rs @@ -42,7 +42,7 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { let r = ffi::SleepConditionVariableSRW(self.inner.get(), mutex::raw(mutex), - dur.num_milliseconds() as DWORD, + super::dur2timeout(dur), 0); if r == 0 { const ERROR_TIMEOUT: DWORD = 0x5B4; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 5ae5f6f201bad..87b3a6f91aba5 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -20,6 +20,7 @@ use libc; use num::Zero; use os::windows::ffi::{OsStrExt, OsStringExt}; use path::PathBuf; +use time::Duration; pub mod backtrace; pub mod c; @@ -151,6 +152,25 @@ fn cvt(i: I) -> io::Result { } } +fn dur2timeout(dur: Duration) -> libc::DWORD { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is lost everywhere + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.secs().checked_mul(1000).and_then(|ms| { + ms.checked_add((dur.nanos() as u64) / 1_000_000) + }).and_then(|ms| { + if ms > ::max_value() as u64 { + libc::INFINITE + } else { + ms as libc::DWORD + } + }).unwrap_or(libc::INFINITE) +} + fn ms_to_filetime(ms: u64) -> libc::FILETIME { // A FILETIME is a count of 100 nanosecond intervals, so we multiply by // 10000 b/c there are 10000 intervals in 1 ms diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 797f45f8702e7..50dfee4ab1011 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -80,15 +80,7 @@ impl Thread { pub fn sleep(dur: Duration) { unsafe { - if dur < Duration::zero() { - return Thread::yield_now() - } - let ms = dur.num_milliseconds(); - // if we have a fractional number of milliseconds then add an extra - // millisecond to sleep for - let extra = dur - Duration::milliseconds(ms); - let ms = ms + if extra.is_zero() {0} else {1}; - c::Sleep(ms as DWORD); + c::Sleep(super::dur2timeout(dur)) } } } diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 209460df10b7c..8212e5f0cec8a 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -12,7 +12,7 @@ use ops::Sub; use time::Duration; use sync::{Once, ONCE_INIT}; -const NANOS_PER_SEC: i64 = 1_000_000_000; +const NANOS_PER_SEC: u64 = 1_000_000_000; pub struct SteadyTime { t: libc::LARGE_INTEGER, @@ -24,10 +24,6 @@ impl SteadyTime { unsafe { libc::QueryPerformanceCounter(&mut t.t); } t } - - pub fn ns(&self) -> u64 { - mul_div_i64(self.t as i64, NANOS_PER_SEC, frequency() as i64) as u64 - } } fn frequency() -> libc::LARGE_INTEGER { @@ -46,15 +42,16 @@ impl<'a> Sub for &'a SteadyTime { type Output = Duration; fn sub(self, other: &SteadyTime) -> Duration { - let diff = self.t as i64 - other.t as i64; - Duration::nanoseconds(mul_div_i64(diff, NANOS_PER_SEC, frequency() as i64)) + 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 / 1_000_000_000, (nanos % 1_000_000_000) 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_i64(value: i64, numer: i64, denom: i64) -> i64 { +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), @@ -65,9 +62,6 @@ fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 { #[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); + assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), + 1_000_000_000_001_000); } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index ce531fb13812c..da985a50ef6f1 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -482,7 +482,18 @@ pub fn catch_panic(f: F) -> Result /// spurious wakeup. #[stable(feature = "rust1", since = "1.0.0")] pub fn sleep_ms(ms: u32) { - imp::Thread::sleep(Duration::milliseconds(ms as i64)) + sleep(Duration::from_millis(ms as u64)) +} + +/// Puts the current thread to sleep for the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. Note that on unix platforms +/// this function will not return early due to a signal being received or a +/// spurious wakeup. +#[unstable(feature = "thread_sleep", reason = "waiting on Duration")] +pub fn sleep(dur: Duration) { + imp::Thread::sleep(dur) } /// Blocks unless or until the current thread's token is made available (may wake spuriously). @@ -516,10 +527,25 @@ pub fn park() { /// See the module doc for more detail. #[stable(feature = "rust1", since = "1.0.0")] pub fn park_timeout_ms(ms: u32) { + park_timeout(Duration::from_millis(ms as u64)) +} + +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to `park()` except that the +/// thread will be blocked for roughly no longer than *duration*. This method +/// should not be used for precise timing due to anomalies such as +/// preemption or platform differences that may not cause the maximum +/// amount of time waited to be precisely *duration* long. +/// +/// See the module doc for more detail. +#[unstable(feature = "park_timeout", reason = "waiting on Duration")] +pub fn park_timeout(dur: Duration) { let thread = current(); let mut guard = thread.inner.lock.lock().unwrap(); if !*guard { - let (g, _) = thread.inner.cvar.wait_timeout_ms(guard, ms).unwrap(); + let (g, _) = thread.inner.cvar.wait_timeout(guard, dur).unwrap(); guard = g; } *guard = false; diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 636a0dd697a2b..8778869e557ba 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -10,296 +10,95 @@ //! Temporal quantification -#![unstable(feature = "std_misc")] +#![unstable(feature = "duration", reason = "recently added API per RFC 1040")] use prelude::v1::*; -use {fmt, i64}; -use ops::{Add, Sub, Mul, Div, Neg}; - -/// The number of nanoseconds in a microsecond. -const NANOS_PER_MICRO: i32 = 1000; -/// The number of nanoseconds in a millisecond. -const NANOS_PER_MILLI: i32 = 1000_000; -/// The number of nanoseconds in seconds. -const NANOS_PER_SEC: i32 = 1_000_000_000; -/// The number of microseconds per second. -const MICROS_PER_SEC: i64 = 1000_000; -/// The number of milliseconds per second. -const MILLIS_PER_SEC: i64 = 1000; -/// The number of seconds in a minute. -const SECS_PER_MINUTE: i64 = 60; -/// The number of seconds in an hour. -const SECS_PER_HOUR: i64 = 3600; -/// The number of (non-leap) seconds in days. -const SECS_PER_DAY: i64 = 86400; -/// The number of (non-leap) seconds in a week. -const SECS_PER_WEEK: i64 = 604800; - -macro_rules! try_opt { - ($e:expr) => (match $e { Some(v) => v, None => return None }) -} - - -/// ISO 8601 time duration with nanosecond precision. -/// This also allows for the negative duration; see individual methods for details. -#[unstable(feature = "std_misc")] +use fmt; +use ops::{Add, Sub, Mul, Div}; +use sys::time::SteadyTime; + +const NANOS_PER_SEC: u32 = 1_000_000_000; +const NANOS_PER_MILLI: u32 = 1_000_000; +const MILLIS_PER_SEC: u64 = 1_000; + +/// A duration type to represent a span of time, typically used for system +/// timeouts. +/// +/// Each duration is composed of a number of seconds and nanosecond precision. +/// APIs binding a system timeout will typically truncate the nanosecond +/// precision if the underlying system does not support that level of precision. +/// +/// Durations implement many common traits, including `Add`, `Sub`, and other +/// ops traits. Currently a duration may only be inspected for its number of +/// seconds and its nanosecond precision. +/// +/// # Examples +/// +/// ``` +/// #![feature(duration)] +/// use std::time::Duration; +/// +/// let five_seconds = Duration::new(5, 0); +/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); +/// +/// assert_eq!(five_seconds_and_five_nanos.secs(), 5); +/// assert_eq!(five_seconds_and_five_nanos.nanos(), 5); +/// +/// let ten_millis = Duration::from_millis(10); +/// ``` #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Duration { - secs: i64, - nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC + secs: u64, + nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC } -/// The minimum possible `Duration`: `i64::MIN` milliseconds. -#[unstable(feature = "std_misc")] -pub const MIN: Duration = Duration { - secs: i64::MIN / MILLIS_PER_SEC - 1, - nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI -}; - -/// The maximum possible `Duration`: `i64::MAX` milliseconds. -#[unstable(feature = "std_misc")] -pub const MAX: Duration = Duration { - secs: i64::MAX / MILLIS_PER_SEC, - nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI -}; - impl Duration { - /// Makes a new `Duration` with given number of weeks. - /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn weeks(weeks: i64) -> Duration { - let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of days. - /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn days(days: i64) -> Duration { - let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of hours. - /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn hours(hours: i64) -> Duration { - let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of minutes. - /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. - /// Panics when the duration is out of bounds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn minutes(minutes: i64) -> Duration { - let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); - Duration::seconds(secs) - } - - /// Makes a new `Duration` with given number of seconds. - /// Panics when the duration is more than `i64::MAX` milliseconds - /// or less than `i64::MIN` milliseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn seconds(seconds: i64) -> Duration { - let d = Duration { secs: seconds, nanos: 0 }; - if d < MIN || d > MAX { - panic!("Duration::seconds out of bounds"); - } - d - } - - /// Makes a new `Duration` with given number of milliseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn milliseconds(milliseconds: i64) -> Duration { - let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); - let nanos = millis as i32 * NANOS_PER_MILLI; + /// Crates a new `Duration` from the specified number of seconds and + /// additional nanosecond precision. + /// + /// If the nanoseconds is greater than 1 billion (the number of nanoseconds + /// in a second), then it will carry over into the seconds provided. + pub fn new(secs: u64, nanos: u32) -> Duration { + let secs = secs + (nanos / NANOS_PER_SEC) as u64; + let nanos = nanos % NANOS_PER_SEC; Duration { secs: secs, nanos: nanos } } - /// Makes a new `Duration` with given number of microseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn microseconds(microseconds: i64) -> Duration { - let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - let nanos = micros as i32 * NANOS_PER_MICRO; - Duration { secs: secs, nanos: nanos } - } - - /// Makes a new `Duration` with given number of nanoseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn nanoseconds(nanos: i64) -> Duration { - let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); - Duration { secs: secs, nanos: nanos as i32 } - } - /// Runs a closure, returning the duration of time it took to run the /// closure. - #[unstable(feature = "std_misc")] pub fn span(f: F) -> Duration where F: FnOnce() { - let before = super::precise_time_ns(); + let start = SteadyTime::now(); f(); - Duration::nanoseconds((super::precise_time_ns() - before) as i64) + &SteadyTime::now() - &start } - /// Returns the total number of whole weeks in the duration. - #[inline] - #[unstable(feature = "std_misc")] - pub fn num_weeks(&self) -> i64 { - self.num_days() / 7 + /// Creates a new `Duration` from the specified number of seconds. + pub fn from_secs(secs: u64) -> Duration { + Duration { secs: secs, nanos: 0 } } - /// Returns the total number of whole days in the duration. - #[unstable(feature = "std_misc")] - pub fn num_days(&self) -> i64 { - self.num_seconds() / SECS_PER_DAY - } - - /// Returns the total number of whole hours in the duration. - #[inline] - #[unstable(feature = "std_misc")] - pub fn num_hours(&self) -> i64 { - self.num_seconds() / SECS_PER_HOUR - } - - /// Returns the total number of whole minutes in the duration. - #[inline] - #[unstable(feature = "std_misc")] - pub fn num_minutes(&self) -> i64 { - self.num_seconds() / SECS_PER_MINUTE - } - - /// Returns the total number of whole seconds in the duration. - #[unstable(feature = "std_misc")] - pub fn num_seconds(&self) -> i64 { - // If secs is negative, nanos should be subtracted from the duration. - if self.secs < 0 && self.nanos > 0 { - self.secs + 1 - } else { - self.secs - } - } - - /// Returns the number of nanoseconds such that - /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of - /// nanoseconds in the duration. - fn nanos_mod_sec(&self) -> i32 { - if self.secs < 0 && self.nanos > 0 { - self.nanos - NANOS_PER_SEC - } else { - self.nanos - } - } - - /// Returns the total number of whole milliseconds in the duration, - #[unstable(feature = "std_misc")] - pub fn num_milliseconds(&self) -> i64 { - // A proper Duration will not overflow, because MIN and MAX are defined - // such that the range is exactly i64 milliseconds. - let secs_part = self.num_seconds() * MILLIS_PER_SEC; - let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; - secs_part + nanos_part as i64 - } - - /// Returns the total number of whole microseconds in the duration, - /// or `None` on overflow (exceeding 2^63 microseconds in either direction). - #[unstable(feature = "std_misc")] - pub fn num_microseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); - let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; - secs_part.checked_add(nanos_part as i64) - } - - /// Returns the total number of whole nanoseconds in the duration, - /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). - #[unstable(feature = "std_misc")] - pub fn num_nanoseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); - let nanos_part = self.nanos_mod_sec(); - secs_part.checked_add(nanos_part as i64) - } - - /// Add two durations, returning `None` if overflow occurred. - #[unstable(feature = "std_misc")] - pub fn checked_add(&self, rhs: &Duration) -> Option { - let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs = try_opt!(secs.checked_add(1)); - } - let d = Duration { secs: secs, nanos: nanos }; - // Even if d is within the bounds of i64 seconds, - // it might still overflow i64 milliseconds. - if d < MIN || d > MAX { None } else { Some(d) } + /// Creates a new `Duration` from the specified number of milliseconds. + pub fn from_millis(millis: u64) -> Duration { + let secs = millis / MILLIS_PER_SEC; + let nanos = ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI; + Duration { secs: secs, nanos: nanos } } - /// Subtract two durations, returning `None` if overflow occurred. - #[unstable(feature = "std_misc")] - pub fn checked_sub(&self, rhs: &Duration) -> Option { - let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); - let mut nanos = self.nanos - rhs.nanos; - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs = try_opt!(secs.checked_sub(1)); - } - let d = Duration { secs: secs, nanos: nanos }; - // Even if d is within the bounds of i64 seconds, - // it might still overflow i64 milliseconds. - if d < MIN || d > MAX { None } else { Some(d) } - } + /// Returns the number of whole seconds represented by this duration. + /// + /// The nanosecond precision represented by this duration is not returned + /// and can be learned by calling `.nanos()` + pub fn secs(&self) -> u64 { self.secs } - /// The minimum possible `Duration`: `i64::MIN` milliseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn min_value() -> Duration { MIN } - - /// The maximum possible `Duration`: `i64::MAX` milliseconds. - #[inline] - #[unstable(feature = "std_misc")] - pub fn max_value() -> Duration { MAX } - - /// A duration where the stored seconds and nanoseconds are equal to zero. - #[inline] - #[unstable(feature = "std_misc")] - pub fn zero() -> Duration { - Duration { secs: 0, nanos: 0 } - } - - /// Returns `true` if the duration equals `Duration::zero()`. - #[inline] - #[unstable(feature = "std_misc")] - pub fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 - } + /// Returns the nanosecond precision represented by this duration. + /// + /// This method does **not** return the length of the duration when + /// represented by nanoseconds. The returned number is always between 0 and + /// 1 billion (the number of nanoseconds in a second). + pub fn nanos(&self) -> u32 { self.nanos } } -#[unstable(feature = "std_misc")] -impl Neg for Duration { - type Output = Duration; - - #[inline] - fn neg(self) -> Duration { - if self.nanos == 0 { - Duration { secs: -self.secs, nanos: 0 } - } else { - Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } - } - } -} - -#[unstable(feature = "std_misc")] impl Add for Duration { type Output = Duration; @@ -310,289 +109,170 @@ impl Add for Duration { nanos -= NANOS_PER_SEC; secs += 1; } + debug_assert!(nanos < NANOS_PER_SEC); Duration { secs: secs, nanos: nanos } } } -#[unstable(feature = "std_misc")] impl Sub for Duration { type Output = Duration; fn sub(self, rhs: Duration) -> Duration { - let mut secs = self.secs - rhs.secs; - let mut nanos = self.nanos - rhs.nanos; - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } + let mut secs = match self.secs.checked_sub(rhs.secs) { + Some(secs) => secs, + None => panic!("duration subtraction resulted in negative duration"), + }; + let nanos = if self.nanos >= rhs.nanos { + self.nanos - rhs.nanos + } else { + if secs > 0 { + secs -= 1; + self.nanos + NANOS_PER_SEC - rhs.nanos + } else { + panic!("duration subtraction resulted in negative duration") + } + }; + debug_assert!(nanos < NANOS_PER_SEC); Duration { secs: secs, nanos: nanos } } } -#[unstable(feature = "std_misc")] -impl Mul for Duration { +impl Mul for Duration { type Output = Duration; - fn mul(self, rhs: i32) -> Duration { - // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanos as i64 * rhs as i64; - let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); - let secs = self.secs * rhs as i64 + extra_secs; - Duration { secs: secs, nanos: nanos as i32 } + fn mul(self, rhs: u32) -> Duration { + // Multiply nanoseconds as u64, because it cannot overflow that way. + let total_nanos = self.nanos as u64 * rhs as u64; + let extra_secs = total_nanos / (NANOS_PER_SEC as u64); + let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; + let secs = self.secs * rhs as u64 + extra_secs; + debug_assert!(nanos < NANOS_PER_SEC); + Duration { secs: secs, nanos: nanos } } } -#[unstable(feature = "std_misc")] -impl Div for Duration { +impl Div for Duration { type Output = Duration; - fn div(self, rhs: i32) -> Duration { - let mut secs = self.secs / rhs as i64; - let carry = self.secs - secs * rhs as i64; - let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; - let mut nanos = self.nanos / rhs + extra_nanos as i32; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs += 1; - } - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } + fn div(self, rhs: u32) -> Duration { + let secs = self.secs / (rhs as u64); + let carry = self.secs - secs * (rhs as u64); + let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); + let nanos = self.nanos / rhs + (extra_nanos as u32); + debug_assert!(nanos < NANOS_PER_SEC); Duration { secs: secs, nanos: nanos } } } -#[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Duration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // technically speaking, negative duration is not valid ISO 8601, - // but we need to print it anyway. - let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; - - let days = abs.secs / SECS_PER_DAY; - let secs = abs.secs - days * SECS_PER_DAY; - let hasdate = days != 0; - let hastime = (secs != 0 || abs.nanos != 0) || !hasdate; - - try!(write!(f, "{}P", sign)); - - if hasdate { - try!(write!(f, "{}D", days)); - } - if hastime { - if abs.nanos == 0 { - try!(write!(f, "T{}S", secs)); - } else if abs.nanos % NANOS_PER_MILLI == 0 { - try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)); - } else if abs.nanos % NANOS_PER_MICRO == 0 { - try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)); - } else { - try!(write!(f, "T{}.{:09}S", secs, abs.nanos)); + let seconds = self.secs; + let millis = self.nanos / NANOS_PER_MILLI; + let nanos = self.nanos % NANOS_PER_MILLI; + match (seconds, millis, nanos) { + (s, 0, 0) => write!(f, "{} seconds", s), + (0, m, 0) => write!(f, "{} milliseconds", m), + (0, 0, n) => write!(f, "{} nanoseconds", n), + (s, m, 0) => write!(f, "{} seconds and {} milliseconds", s, m), + (s, 0, n) => write!(f, "{} seconds and {} nanoseconds", s, n), + (0, m, n) => write!(f, "{} milliseconds and {} nanoseconds", m, n), + (s, m, n) => { + write!(f, "{} seconds, {} milliseconds, and {} nanoseconds", + s, m, n) } } - Ok(()) } } -// Copied from libnum -#[inline] -fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { - (div_floor_64(this, other), mod_floor_64(this, other)) -} - -#[inline] -fn div_floor_64(this: i64, other: i64) -> i64 { - match div_rem_64(this, other) { - (d, r) if (r > 0 && other < 0) - || (r < 0 && other > 0) => d - 1, - (d, _) => d, - } -} - -#[inline] -fn mod_floor_64(this: i64, other: i64) -> i64 { - match this % other { - r if (r > 0 && other < 0) - || (r < 0 && other > 0) => r + other, - r => r, - } -} - -#[inline] -fn div_rem_64(this: i64, other: i64) -> (i64, i64) { - (this / other, this % other) -} - #[cfg(test)] mod tests { - use super::{Duration, MIN, MAX}; - use {i32, i64}; - use option::Option::{Some, None}; - use string::ToString; + use prelude::v1::*; + use super::Duration; #[test] - fn test_duration() { - assert!(Duration::seconds(1) != Duration::zero()); - assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3)); - assert_eq!(Duration::seconds(86399) + Duration::seconds(4), - Duration::days(1) + Duration::seconds(3)); - assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000)); - assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000)); - assert_eq!(Duration::days(2) + Duration::seconds(86399) + - Duration::nanoseconds(1234567890), - Duration::days(3) + Duration::nanoseconds(234567890)); - assert_eq!(-Duration::days(3), Duration::days(-3)); - assert_eq!(-(Duration::days(3) + Duration::seconds(70)), - Duration::days(-4) + Duration::seconds(86400-70)); + fn creation() { + assert!(Duration::from_secs(1) != Duration::from_secs(0)); + assert_eq!(Duration::from_secs(1) + Duration::from_secs(2), + Duration::from_secs(3)); + assert_eq!(Duration::from_millis(10) + Duration::from_secs(4), + Duration::new(4, 10 * 1_000_000)); + assert_eq!(Duration::from_millis(4000), Duration::new(4, 0)); } #[test] - fn test_duration_num_days() { - assert_eq!(Duration::zero().num_days(), 0); - assert_eq!(Duration::days(1).num_days(), 1); - assert_eq!(Duration::days(-1).num_days(), -1); - assert_eq!(Duration::seconds(86399).num_days(), 0); - assert_eq!(Duration::seconds(86401).num_days(), 1); - assert_eq!(Duration::seconds(-86399).num_days(), 0); - assert_eq!(Duration::seconds(-86401).num_days(), -1); - assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64); - assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); + fn secs() { + assert_eq!(Duration::new(0, 0).secs(), 0); + assert_eq!(Duration::from_secs(1).secs(), 1); + assert_eq!(Duration::from_millis(999).secs(), 0); + assert_eq!(Duration::from_millis(1001).secs(), 1); } #[test] - fn test_duration_num_seconds() { - assert_eq!(Duration::zero().num_seconds(), 0); - assert_eq!(Duration::seconds(1).num_seconds(), 1); - assert_eq!(Duration::seconds(-1).num_seconds(), -1); - assert_eq!(Duration::milliseconds(999).num_seconds(), 0); - assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); - assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); - assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); + fn nanos() { + assert_eq!(Duration::new(0, 0).nanos(), 0); + assert_eq!(Duration::new(0, 5).nanos(), 5); + assert_eq!(Duration::new(0, 1_000_000_001).nanos(), 1); + assert_eq!(Duration::from_secs(1).nanos(), 0); + assert_eq!(Duration::from_millis(999).nanos(), 999 * 1_000_000); + assert_eq!(Duration::from_millis(1001).nanos(), 1 * 1_000_000); } #[test] - fn test_duration_num_milliseconds() { - assert_eq!(Duration::zero().num_milliseconds(), 0); - assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1); - assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1); - assert_eq!(Duration::microseconds(999).num_milliseconds(), 0); - assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); - assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); - assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); - assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX); - assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); - assert_eq!(MAX.num_milliseconds(), i64::MAX); - assert_eq!(MIN.num_milliseconds(), i64::MIN); + fn add() { + assert_eq!(Duration::new(0, 0) + Duration::new(0, 1), + Duration::new(0, 1)); + assert_eq!(Duration::new(0, 500_000_000) + Duration::new(0, 500_000_001), + Duration::new(1, 1)); } #[test] - fn test_duration_num_microseconds() { - assert_eq!(Duration::zero().num_microseconds(), Some(0)); - assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1)); - assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1)); - assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0)); - assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1)); - assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0)); - assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1)); - assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX)); - assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); - assert_eq!(MAX.num_microseconds(), None); - assert_eq!(MIN.num_microseconds(), None); - - // overflow checks - const MICROS_PER_DAY: i64 = 86400_000_000; - assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), - Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), - Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); - assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); + fn sub() { + assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), + Duration::new(0, 1)); + assert_eq!(Duration::new(0, 500_000_001) - Duration::new(0, 500_000_000), + Duration::new(0, 1)); + assert_eq!(Duration::new(1, 0) - Duration::new(0, 1), + Duration::new(0, 999_999_999)); } - #[test] - fn test_duration_num_nanoseconds() { - assert_eq!(Duration::zero().num_nanoseconds(), Some(0)); - assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1)); - assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1)); - assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX)); - assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); - assert_eq!(MAX.num_nanoseconds(), None); - assert_eq!(MIN.num_nanoseconds(), None); - - // overflow checks - const NANOS_PER_DAY: i64 = 86400_000_000_000; - assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), - Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), - Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); - assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); + #[test] #[should_panic] + fn sub_bad1() { + Duration::new(0, 0) - Duration::new(0, 1); } - #[test] - fn test_duration_checked_ops() { - assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), - Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); - assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)) - .is_none()); - - assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), - Some(Duration::milliseconds(i64::MIN))); - assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)) - .is_none()); + #[test] #[should_panic] + fn sub_bad2() { + Duration::new(0, 0) - Duration::new(1, 0); } #[test] - fn test_duration_mul() { - assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); - assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); - assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero()); - assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1)); - assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1)); - assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1)); - assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1)); - assert_eq!(Duration::nanoseconds(30) * 333_333_333, - Duration::seconds(10) - Duration::nanoseconds(10)); - assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3, - Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)); - assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); - assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); + fn mul() { + assert_eq!(Duration::new(0, 1) * 2, Duration::new(0, 2)); + assert_eq!(Duration::new(1, 1) * 3, Duration::new(3, 3)); + assert_eq!(Duration::new(0, 500_000_001) * 4, Duration::new(2, 4)); + assert_eq!(Duration::new(0, 500_000_001) * 4000, + Duration::new(2000, 4000)); } #[test] - fn test_duration_div() { - assert_eq!(Duration::zero() / i32::MAX, Duration::zero()); - assert_eq!(Duration::zero() / i32::MIN, Duration::zero()); - assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789)); - assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); - assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); - assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); - assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); - assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); - assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); - assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); - assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); - assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); - assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); + fn div() { + assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); + assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333)); + assert_eq!(Duration::new(99, 999_999_000) / 100, + Duration::new(0, 999_999_990)); } #[test] - fn test_duration_fmt() { - assert_eq!(Duration::zero().to_string(), "PT0S"); - assert_eq!(Duration::days(42).to_string(), "P42D"); - assert_eq!(Duration::days(-42).to_string(), "-P42D"); - assert_eq!(Duration::seconds(42).to_string(), "PT42S"); - assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); - assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); - assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); - assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), - "P7DT6.543S"); - assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); - assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); - - // the format specifier should have no effect on `Duration` - assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), - "P1DT2.345S"); + fn display() { + assert_eq!(Duration::new(0, 2).to_string(), "2 nanoseconds"); + assert_eq!(Duration::new(0, 2_000_000).to_string(), "2 milliseconds"); + assert_eq!(Duration::new(2, 0).to_string(), "2 seconds"); + assert_eq!(Duration::new(2, 2).to_string(), "2 seconds and 2 nanoseconds"); + assert_eq!(Duration::new(2, 2_000_000).to_string(), + "2 seconds and 2 milliseconds"); + assert_eq!(Duration::new(0, 2_000_002).to_string(), + "2 milliseconds and 2 nanoseconds"); + assert_eq!(Duration::new(2, 2_000_002).to_string(), + "2 seconds, 2 milliseconds, and 2 nanoseconds"); } } diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index 4d9bb8050d31e..d535b19551995 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -10,17 +10,8 @@ //! Temporal quantification. -#![unstable(feature = "std_misc")] - -use sys::time::SteadyTime; +#![unstable(feature = "time")] pub use self::duration::Duration; -pub mod duration; - -/// Returns the current value of a high-resolution performance counter -/// in nanoseconds since an unspecified epoch. -// NB: this is intentionally not public, this is not ready to stabilize its api. -fn precise_time_ns() -> u64 { - SteadyTime::now().ns() -} +mod duration; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index b68b1d28b3567..155ed00586112 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -43,6 +43,7 @@ #![feature(std_misc)] #![feature(libc)] #![feature(set_stdio)] +#![feature(duration)] extern crate getopts; extern crate serialize; @@ -1047,7 +1048,7 @@ impl Bencher { } pub fn ns_elapsed(&mut self) -> u64 { - self.dur.num_nanoseconds().unwrap() as u64 + self.dur.secs() * 1_000_000_000 + (self.dur.nanos() as u64) } pub fn ns_per_iter(&mut self) -> u64 { @@ -1083,7 +1084,7 @@ impl Bencher { // (i.e. larger error bars). if n == 0 { n = 1; } - let mut total_run = Duration::nanoseconds(0); + let mut total_run = Duration::new(0, 0); let samples : &mut [f64] = &mut [0.0_f64; 50]; loop { let mut summ = None; @@ -1112,7 +1113,7 @@ impl Bencher { // If we've run for 100ms and seem to have converged to a // stable median. - if loop_run.num_milliseconds() > 100 && + if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 && summ.median - summ5.median < summ5.median_abs_dev { return summ5; @@ -1120,7 +1121,7 @@ impl Bencher { total_run = total_run + loop_run; // Longest we ever run for is 3s. - if total_run.num_seconds() > 3 { + if total_run > Duration::from_secs(3) { return summ5; } @@ -1144,7 +1145,7 @@ pub mod bench { pub fn benchmark(f: F) -> BenchSamples where F: FnMut(&mut Bencher) { let mut bs = Bencher { iterations: 0, - dur: Duration::nanoseconds(0), + dur: Duration::new(0, 0), bytes: 0 }; diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs index 8f3e939f1f40d..c003b864771e2 100644 --- a/src/test/bench/core-map.rs +++ b/src/test/bench/core-map.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(std_misc, rand)] +#![feature(std_misc, rand, duration)] use std::collections::{BTreeMap, HashMap, HashSet}; use std::env; diff --git a/src/test/bench/core-set.rs b/src/test/bench/core-set.rs index aeedaa288fef8..01e76a0fb573a 100644 --- a/src/test/bench/core-set.rs +++ b/src/test/bench/core-set.rs @@ -10,7 +10,7 @@ // ignore-pretty very bad with line comments -#![feature(unboxed_closures, rand, std_misc, collections)] +#![feature(unboxed_closures, rand, std_misc, collections, duration)] extern crate collections; extern crate rand; @@ -167,13 +167,13 @@ fn write_results(label: &str, results: &Results) { fn empty_results() -> Results { Results { - sequential_ints: Duration::seconds(0), - random_ints: Duration::seconds(0), - delete_ints: Duration::seconds(0), + sequential_ints: Duration::new(0, 0), + random_ints: Duration::new(0, 0), + delete_ints: Duration::new(0, 0), - sequential_strings: Duration::seconds(0), - random_strings: Duration::seconds(0), - delete_strings: Duration::seconds(0), + sequential_strings: Duration::new(0, 0), + random_strings: Duration::new(0, 0), + delete_strings: Duration::new(0, 0), } } diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs index 19f83c7817c7c..85c7a9fd1ad30 100644 --- a/src/test/bench/core-std.rs +++ b/src/test/bench/core-std.rs @@ -10,7 +10,7 @@ // Microbenchmarks for various functions in std and extra -#![feature(rand, collections, std_misc)] +#![feature(rand, collections, std_misc, duration)] use std::iter::repeat; use std::mem::swap; @@ -52,7 +52,7 @@ fn maybe_run_test(argv: &[String], name: String, test: F) where F: FnOnce() { let dur = Duration::span(test); - println!("{}:\t\t{} ms", name, dur.num_milliseconds()); + println!("{}:\t\t{}", name, dur); } fn shift_push() { diff --git a/src/test/bench/msgsend-pipes-shared.rs b/src/test/bench/msgsend-pipes-shared.rs index c7748d59c6a04..9e8e8d54f6893 100644 --- a/src/test/bench/msgsend-pipes-shared.rs +++ b/src/test/bench/msgsend-pipes-shared.rs @@ -18,7 +18,7 @@ // different scalability characteristics compared to the select // version. -#![feature(std_misc)] +#![feature(duration)] use std::sync::mpsc::{channel, Sender, Receiver}; use std::env; @@ -88,9 +88,9 @@ fn run(args: &[String]) { }); let result = result.unwrap(); print!("Count is {}\n", result); - print!("Test took {} ms\n", dur.num_milliseconds()); - let thruput = ((size / workers * workers) as f64) / (dur.num_milliseconds() as f64); - print!("Throughput={} per sec\n", thruput / 1000.0); + print!("Test took {}\n", dur); + let thruput = ((size / workers * workers) as f64) / (dur.secs() as f64); + print!("Throughput={} per sec\n", thruput); assert_eq!(result, num_bytes * size); } diff --git a/src/test/bench/msgsend-pipes.rs b/src/test/bench/msgsend-pipes.rs index b6a6e06088a09..06f401aed52a6 100644 --- a/src/test/bench/msgsend-pipes.rs +++ b/src/test/bench/msgsend-pipes.rs @@ -14,7 +14,7 @@ // // I *think* it's the same, more or less. -#![feature(std_misc)] +#![feature(duration)] use std::sync::mpsc::{channel, Sender, Receiver}; use std::env; @@ -95,9 +95,9 @@ fn run(args: &[String]) { }); let result = result.unwrap(); print!("Count is {}\n", result); - print!("Test took {} ms\n", dur.num_milliseconds()); - let thruput = ((size / workers * workers) as f64) / (dur.num_milliseconds() as f64); - print!("Throughput={} per sec\n", thruput / 1000.0); + print!("Test took {}\n", dur); + let thruput = ((size / workers * workers) as f64) / (dur.secs() as f64); + print!("Throughput={} per sec\n", thruput); assert_eq!(result, num_bytes * size); } diff --git a/src/test/bench/msgsend-ring-mutex-arcs.rs b/src/test/bench/msgsend-ring-mutex-arcs.rs index 8048f3dde9682..6c7c9ff5502bc 100644 --- a/src/test/bench/msgsend-ring-mutex-arcs.rs +++ b/src/test/bench/msgsend-ring-mutex-arcs.rs @@ -17,7 +17,7 @@ // no-pretty-expanded FIXME #15189 -#![feature(std_misc)] +#![feature(duration, std_misc)] use std::env; use std::sync::{Arc, Future, Mutex, Condvar}; @@ -107,9 +107,9 @@ fn main() { // all done, report stats. let num_msgs = num_tasks * msg_per_task; - let rate = (num_msgs as f64) / (dur.num_milliseconds() as f64); + let rate = (num_msgs as f64) / (dur.secs() as f64); - println!("Sent {} messages in {} ms", num_msgs, dur.num_milliseconds()); - println!(" {} messages / second", rate / 1000.0); - println!(" {} μs / message", 1000000. / rate / 1000.0); + println!("Sent {} messages in {}", num_msgs, dur); + println!(" {} messages / second", rate); + println!(" {} μs / message", 1000000. / rate); } diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index ed20f4b6362ce..6a4e2f79cd447 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -18,7 +18,7 @@ */ -#![feature(std_misc, rustc_private)] +#![feature(duration, rustc_private)] extern crate getopts; diff --git a/src/test/bench/std-smallintmap.rs b/src/test/bench/std-smallintmap.rs index dd56b18c144f2..f8f1d20346bd1 100644 --- a/src/test/bench/std-smallintmap.rs +++ b/src/test/bench/std-smallintmap.rs @@ -10,7 +10,7 @@ // Microbenchmark for the smallintmap library -#![feature(collections, std_misc)] +#![feature(collections, duration)] use std::collections::VecMap; use std::env; @@ -40,8 +40,8 @@ fn main() { let max = args[1].parse::().unwrap(); let rep = args[2].parse::().unwrap(); - let mut checkf = Duration::seconds(0); - let mut appendf = Duration::seconds(0); + let mut checkf = Duration::new(0, 0); + let mut appendf = Duration::new(0, 0); for _ in 0..rep { let mut map = VecMap::new(); @@ -55,7 +55,7 @@ fn main() { let maxf = max as f64; println!("insert(): {} seconds\n", checkf); - println!(" : {} op/ms\n", maxf / checkf.num_milliseconds() as f64); + println!(" : {} op/s\n", maxf / checkf.secs() as f64); println!("get() : {} seconds\n", appendf); - println!(" : {} op/ms\n", maxf / appendf.num_milliseconds() as f64); + println!(" : {} op/s\n", maxf / appendf.secs() as f64); } diff --git a/src/test/bench/task-perf-alloc-unwind.rs b/src/test/bench/task-perf-alloc-unwind.rs index 9eba2c3639039..f4b68d9872de0 100644 --- a/src/test/bench/task-perf-alloc-unwind.rs +++ b/src/test/bench/task-perf-alloc-unwind.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(unsafe_destructor, box_syntax, std_misc, collections)] +#![feature(unsafe_destructor, box_syntax, duration, collections)] use std::env; use std::thread; diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs index df7cedd1c2938..83ce0db365f8b 100644 --- a/src/test/run-pass/core-run-destroy.rs +++ b/src/test/run-pass/core-run-destroy.rs @@ -16,7 +16,7 @@ // instead of in std. #![reexport_test_harness_main = "test_main"] -#![feature(libc, std_misc)] +#![feature(libc, std_misc, duration)] extern crate libc; diff --git a/src/test/run-pass/std-sync-right-kind-impls.rs b/src/test/run-pass/std-sync-right-kind-impls.rs index 058777bb05e5e..36314c5e14ac2 100644 --- a/src/test/run-pass/std-sync-right-kind-impls.rs +++ b/src/test/run-pass/std-sync-right-kind-impls.rs @@ -10,7 +10,7 @@ // pretty-expanded FIXME #23616 -#![feature(std_misc, alloc)] +#![feature(std_misc, alloc, static_condvar)] use std::sync;