Skip to content

Commit c0d3aa8

Browse files
committed
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
1 parent cadc67e commit c0d3aa8

26 files changed

+347
-619
lines changed

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#![feature(path_ext)]
4141
#![feature(str_char)]
4242
#![feature(into_cow)]
43+
#![feature(duration)]
4344
#![feature(slice_patterns)]
4445
#![cfg_attr(test, feature(test))]
4546

src/librustc/metadata/loader.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,7 @@ fn get_metadata_section(is_osx: bool, filename: &Path) -> Result<MetadataBlob, S
720720
let dur = Duration::span(|| {
721721
ret = Some(get_metadata_section_imp(is_osx, filename));
722722
});
723-
info!("reading {:?} => {}ms", filename.file_name().unwrap(),
724-
dur.num_milliseconds());
723+
info!("reading {:?} => {}", filename.file_name().unwrap(), dur);
725724
return ret.unwrap();;
726725
}
727726

src/librustc/util/common.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ pub fn time<T, U, F>(do_it: bool, what: &str, u: U, f: F) -> T where
5555
};
5656
let rv = rv.unwrap();
5757

58-
println!("{}time: {}.{:03} \t{}", repeat(" ").take(old).collect::<String>(),
59-
dur.num_seconds(), dur.num_milliseconds() % 1000, what);
58+
println!("{}time: {} \t{}", repeat(" ").take(old).collect::<String>(),
59+
dur, what);
6060
DEPTH.with(|slot| slot.set(old));
6161

6262
rv

src/libstd/sync/condvar.rs

+55-14
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,20 @@ pub struct Condvar { inner: Box<StaticCondvar> }
6969
/// # Examples
7070
///
7171
/// ```
72-
/// # #![feature(std_misc)]
72+
/// # #![feature(static_condvar)]
7373
/// use std::sync::{StaticCondvar, CONDVAR_INIT};
7474
///
7575
/// static CVAR: StaticCondvar = CONDVAR_INIT;
7676
/// ```
77-
#[unstable(feature = "std_misc",
77+
#[unstable(feature = "static_condvar",
7878
reason = "may be merged with Condvar in the future")]
7979
pub struct StaticCondvar {
8080
inner: sys::Condvar,
8181
mutex: AtomicUsize,
8282
}
8383

8484
/// Constant initializer for a statically allocated condition variable.
85-
#[unstable(feature = "std_misc",
85+
#[unstable(feature = "static_condvar",
8686
reason = "may be merged with Condvar in the future")]
8787
pub const CONDVAR_INIT: StaticCondvar = StaticCondvar {
8888
inner: sys::CONDVAR_INIT,
@@ -161,6 +161,30 @@ impl Condvar {
161161
}
162162
}
163163

164+
/// Waits on this condition variable for a notification, timing out after a
165+
/// specified duration.
166+
///
167+
/// The semantics of this function are equivalent to `wait()` except that
168+
/// the thread will be blocked for roughly no longer than `dur`. This
169+
/// method should not be used for precise timing due to anomalies such as
170+
/// preemption or platform differences that may not cause the maximum
171+
/// amount of time waited to be precisely `dur`.
172+
///
173+
/// The returned boolean is `false` only if the timeout is known
174+
/// to have elapsed.
175+
///
176+
/// Like `wait`, the lock specified will be re-acquired when this function
177+
/// returns, regardless of whether the timeout elapsed or not.
178+
#[unstable(feature = "wait_timeout", reason = "waiting for Duration")]
179+
pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>,
180+
dur: Duration)
181+
-> LockResult<(MutexGuard<'a, T>, bool)> {
182+
unsafe {
183+
let me: &'static Condvar = &*(self as *const _);
184+
me.inner.wait_timeout(guard, dur)
185+
}
186+
}
187+
164188
/// Waits on this condition variable for a notification, timing out after a
165189
/// specified duration.
166190
///
@@ -214,7 +238,7 @@ impl StaticCondvar {
214238
/// notification.
215239
///
216240
/// See `Condvar::wait`.
217-
#[unstable(feature = "std_misc",
241+
#[unstable(feature = "static_condvar",
218242
reason = "may be merged with Condvar in the future")]
219243
pub fn wait<'a, T>(&'static self, guard: MutexGuard<'a, T>)
220244
-> LockResult<MutexGuard<'a, T>> {
@@ -235,14 +259,27 @@ impl StaticCondvar {
235259
/// specified duration.
236260
///
237261
/// See `Condvar::wait_timeout`.
238-
#[unstable(feature = "std_misc",
262+
#[unstable(feature = "static_condvar",
239263
reason = "may be merged with Condvar in the future")]
240264
pub fn wait_timeout_ms<'a, T>(&'static self, guard: MutexGuard<'a, T>, ms: u32)
241265
-> LockResult<(MutexGuard<'a, T>, bool)> {
266+
self.wait_timeout(guard, Duration::from_millis(ms as u64))
267+
}
268+
269+
/// Waits on this condition variable for a notification, timing out after a
270+
/// specified duration.
271+
///
272+
/// See `Condvar::wait_timeout`.
273+
#[unstable(feature = "static_condvar",
274+
reason = "may be merged with Condvar in the future")]
275+
pub fn wait_timeout<'a, T>(&'static self,
276+
guard: MutexGuard<'a, T>,
277+
timeout: Duration)
278+
-> LockResult<(MutexGuard<'a, T>, bool)> {
242279
let (poisoned, success) = unsafe {
243280
let lock = mutex::guard_lock(&guard);
244281
self.verify(lock);
245-
let success = self.inner.wait_timeout(lock, Duration::milliseconds(ms as i64));
282+
let success = self.inner.wait_timeout(lock, timeout);
246283
(mutex::guard_poison(&guard).get(), success)
247284
};
248285
if poisoned {
@@ -259,15 +296,16 @@ impl StaticCondvar {
259296
/// passed and the function returns `false`.
260297
///
261298
/// See `Condvar::wait_timeout_with`.
262-
#[unstable(feature = "std_misc",
299+
#[unstable(feature = "static_condvar",
263300
reason = "may be merged with Condvar in the future")]
264301
pub fn wait_timeout_with<'a, T, F>(&'static self,
265302
guard: MutexGuard<'a, T>,
266303
dur: Duration,
267304
mut f: F)
268305
-> LockResult<(MutexGuard<'a, T>, bool)>
269306
where F: FnMut(LockResult<&mut T>) -> bool {
270-
// This could be made more efficient by pushing the implementation into sys::condvar
307+
// This could be made more efficient by pushing the implementation into
308+
// sys::condvar
271309
let start = SteadyTime::now();
272310
let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard);
273311
while !f(guard_result
@@ -277,7 +315,7 @@ impl StaticCondvar {
277315
let now = SteadyTime::now();
278316
let consumed = &now - &start;
279317
let guard = guard_result.unwrap_or_else(|e| e.into_inner());
280-
let res = self.wait_timeout_ms(guard, (dur - consumed).num_milliseconds() as u32);
318+
let res = self.wait_timeout(guard, dur - consumed);
281319
let (new_guard_result, no_timeout) = match res {
282320
Ok((new_guard, no_timeout)) => (Ok(new_guard), no_timeout),
283321
Err(err) => {
@@ -301,14 +339,14 @@ impl StaticCondvar {
301339
/// Wakes up one blocked thread on this condvar.
302340
///
303341
/// See `Condvar::notify_one`.
304-
#[unstable(feature = "std_misc",
342+
#[unstable(feature = "static_condvar",
305343
reason = "may be merged with Condvar in the future")]
306344
pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } }
307345

308346
/// Wakes up all blocked threads on this condvar.
309347
///
310348
/// See `Condvar::notify_all`.
311-
#[unstable(feature = "std_misc",
349+
#[unstable(feature = "static_condvar",
312350
reason = "may be merged with Condvar in the future")]
313351
pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } }
314352

@@ -318,7 +356,7 @@ impl StaticCondvar {
318356
/// active users of the condvar, and this also doesn't prevent any future
319357
/// users of the condvar. This method is required to be called to not leak
320358
/// memory on all platforms.
321-
#[unstable(feature = "std_misc",
359+
#[unstable(feature = "static_condvar",
322360
reason = "may be merged with Condvar in the future")]
323361
pub unsafe fn destroy(&'static self) {
324362
self.inner.destroy()
@@ -447,7 +485,9 @@ mod tests {
447485
static S: AtomicUsize = ATOMIC_USIZE_INIT;
448486

449487
let g = M.lock().unwrap();
450-
let (g, success) = C.wait_timeout_with(g, Duration::nanoseconds(1000), |_| false).unwrap();
488+
let (g, success) = C.wait_timeout_with(g, Duration::new(0, 1000), |_| {
489+
false
490+
}).unwrap();
451491
assert!(!success);
452492

453493
let (tx, rx) = channel();
@@ -471,7 +511,8 @@ mod tests {
471511
});
472512

473513
let mut state = 0;
474-
let (_g, success) = C.wait_timeout_with(g, Duration::days(1), |_| {
514+
let day = 24 * 60 * 60;
515+
let (_g, success) = C.wait_timeout_with(g, Duration::new(day, 0), |_| {
475516
assert_eq!(state, S.load(Ordering::SeqCst));
476517
tx.send(()).unwrap();
477518
state += 1;

src/libstd/sys/unix/condvar.rs

+9-13
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,20 @@ impl Condvar {
5757
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
5858
// https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
5959
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
60-
if dur <= Duration::zero() {
61-
return false;
62-
}
63-
64-
// First, figure out what time it currently is, in both system and stable time.
65-
// pthread_cond_timedwait uses system time, but we want to report timeout based on stable
66-
// time.
60+
// First, figure out what time it currently is, in both system and
61+
// stable time. pthread_cond_timedwait uses system time, but we want to
62+
// report timeout based on stable time.
6763
let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
6864
let stable_now = time::SteadyTime::now();
6965
let r = ffi::gettimeofday(&mut sys_now, ptr::null_mut());
7066
debug_assert_eq!(r, 0);
7167

72-
let seconds = dur.num_seconds() as libc::time_t;
68+
let seconds = dur.secs() as libc::time_t;
7369
let timeout = match sys_now.tv_sec.checked_add(seconds) {
7470
Some(sec) => {
7571
libc::timespec {
7672
tv_sec: sec,
77-
tv_nsec: (dur - Duration::seconds(dur.num_seconds()))
78-
.num_nanoseconds().unwrap() as libc::c_long,
73+
tv_nsec: dur.nanos() as libc::c_long,
7974
}
8075
}
8176
None => {
@@ -87,11 +82,12 @@ impl Condvar {
8782
};
8883

8984
// And wait!
90-
let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout);
85+
let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
86+
&timeout);
9187
debug_assert!(r == libc::ETIMEDOUT || r == 0);
9288

93-
// ETIMEDOUT is not a totally reliable method of determining timeout due to clock shifts,
94-
// so do the check ourselves
89+
// ETIMEDOUT is not a totally reliable method of determining timeout due
90+
// to clock shifts, so do the check ourselves
9591
&time::SteadyTime::now() - &stable_now < dur
9692
}
9793

src/libstd/sys/unix/thread.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,9 @@ impl Thread {
129129
}
130130

131131
pub fn sleep(dur: Duration) {
132-
if dur < Duration::zero() {
133-
return Thread::yield_now()
134-
}
135-
let seconds = dur.num_seconds();
136-
let ns = dur - Duration::seconds(seconds);
137132
let mut ts = libc::timespec {
138-
tv_sec: seconds as libc::time_t,
139-
tv_nsec: ns.num_nanoseconds().unwrap() as libc::c_long,
133+
tv_sec: dur.secs() as libc::time_t,
134+
tv_nsec: dur.nanos() as libc::c_long,
140135
};
141136

142137
// If we're awoken with a signal then the return value will be -1 and

src/libstd/sys/unix/time.rs

+9-17
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ mod inner {
3232
t: unsafe { mach_absolute_time() },
3333
}
3434
}
35-
36-
pub fn ns(&self) -> u64 {
37-
let info = info();
38-
self.t * info.numer as u64 / info.denom as u64
39-
}
4035
}
4136

4237
fn info() -> &'static libc::mach_timebase_info {
@@ -59,8 +54,9 @@ mod inner {
5954

6055
fn sub(self, other: &SteadyTime) -> Duration {
6156
let info = info();
62-
let diff = self.t as i64 - other.t as i64;
63-
Duration::nanoseconds(diff * info.numer as i64 / info.denom as i64)
57+
let diff = self.t as u64 - other.t as u64;
58+
let nanos = diff * info.numer as u64 / info.denom as u64;
59+
Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
6460
}
6561
}
6662
}
@@ -71,7 +67,7 @@ mod inner {
7167
use time::Duration;
7268
use ops::Sub;
7369

74-
const NSEC_PER_SEC: i64 = 1_000_000_000;
70+
const NSEC_PER_SEC: u32 = 1_000_000_000;
7571

7672
pub struct SteadyTime {
7773
t: libc::timespec,
@@ -104,23 +100,19 @@ mod inner {
104100
}
105101
t
106102
}
107-
108-
pub fn ns(&self) -> u64 {
109-
self.t.tv_sec as u64 * NSEC_PER_SEC as u64 + self.t.tv_nsec as u64
110-
}
111103
}
112104

113105
impl<'a> Sub for &'a SteadyTime {
114106
type Output = Duration;
115107

116108
fn sub(self, other: &SteadyTime) -> Duration {
117109
if self.t.tv_nsec >= other.t.tv_nsec {
118-
Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) +
119-
Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64)
110+
Duration::new(self.t.tv_sec as u64 - other.t.tv_sec as u64,
111+
self.t.tv_nsec as u32 - other.t.tv_nsec as u32)
120112
} else {
121-
Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) +
122-
Duration::nanoseconds(self.t.tv_nsec as i64 + NSEC_PER_SEC -
123-
other.t.tv_nsec as i64)
113+
Duration::new(self.t.tv_sec as u64 - 1 - other.t.tv_sec as u64,
114+
self.t.tv_nsec as u32 + NSEC_PER_SEC -
115+
other.t.tv_nsec as u32)
124116
}
125117
}
126118
}

src/libstd/sys/windows/condvar.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl Condvar {
4242
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
4343
let r = ffi::SleepConditionVariableSRW(self.inner.get(),
4444
mutex::raw(mutex),
45-
dur.num_milliseconds() as DWORD,
45+
super::dur2timeout(dur),
4646
0);
4747
if r == 0 {
4848
const ERROR_TIMEOUT: DWORD = 0x5B4;

src/libstd/sys/windows/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use libc;
2020
use num::Zero;
2121
use os::windows::ffi::{OsStrExt, OsStringExt};
2222
use path::PathBuf;
23+
use time::Duration;
2324

2425
pub mod backtrace;
2526
pub mod c;
@@ -151,6 +152,25 @@ fn cvt<I: PartialEq + Zero>(i: I) -> io::Result<I> {
151152
}
152153
}
153154

155+
fn dur2timeout(dur: Duration) -> libc::DWORD {
156+
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
157+
// timeouts in windows APIs are typically u32 milliseconds. To translate, we
158+
// have two pieces to take care of:
159+
//
160+
// * Nanosecond precision is lost everywhere
161+
// * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
162+
// (never time out).
163+
dur.secs().checked_mul(1000).and_then(|ms| {
164+
ms.checked_add((dur.nanos() as u64) / 1_000_000)
165+
}).and_then(|ms| {
166+
if ms > <libc::DWORD>::max_value() as u64 {
167+
libc::INFINITE
168+
} else {
169+
ms as libc::DWORD
170+
}
171+
}).unwrap_or(libc::INFINITE)
172+
}
173+
154174
fn ms_to_filetime(ms: u64) -> libc::FILETIME {
155175
// A FILETIME is a count of 100 nanosecond intervals, so we multiply by
156176
// 10000 b/c there are 10000 intervals in 1 ms

src/libstd/sys/windows/thread.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,7 @@ impl Thread {
8080

8181
pub fn sleep(dur: Duration) {
8282
unsafe {
83-
if dur < Duration::zero() {
84-
return Thread::yield_now()
85-
}
86-
let ms = dur.num_milliseconds();
87-
// if we have a fractional number of milliseconds then add an extra
88-
// millisecond to sleep for
89-
let extra = dur - Duration::milliseconds(ms);
90-
let ms = ms + if extra.is_zero() {0} else {1};
91-
c::Sleep(ms as DWORD);
83+
c::Sleep(super::dur2timeout(dur))
9284
}
9385
}
9486
}

0 commit comments

Comments
 (0)