diff options
| author | bors <bors@rust-lang.org> | 2015-01-17 03:51:34 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-01-17 03:51:34 +0000 |
| commit | 378fb5846d2d8dbc5ab24a5e92794c5c39d492dc (patch) | |
| tree | 5e01516c301e9534e68a8d929edfca1e6d75c3b5 /src/libstd/sys | |
| parent | ed530d7a3b67989047e6fe61fe101b9d8158585f (diff) | |
| parent | 08f6380a9f0b866796080094f44fe25ea5636547 (diff) | |
| download | rust-378fb5846d2d8dbc5ab24a5e92794c5c39d492dc.tar.gz rust-378fb5846d2d8dbc5ab24a5e92794c5c39d492dc.zip | |
auto merge of #21132 : sfackler/rust/wait_timeout, r=alexcrichton
**The implementation is a direct adaptation of libcxx's condition_variable implementation.** I also added a wait_timeout_with method, which matches the second overload in C++'s condition_variable. The implementation right now is kind of dumb but it works. There is an outstanding issue with it: as is it doesn't support the use case where a user doesn't care about poisoning and wants to continue through poison. r? @alexcrichton @aturon
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/condvar.rs | 56 | ||||
| -rw-r--r-- | src/libstd/sys/unix/mod.rs | 1 | ||||
| -rw-r--r-- | src/libstd/sys/unix/time.rs | 124 | ||||
| -rw-r--r-- | src/libstd/sys/windows/mod.rs | 1 | ||||
| -rw-r--r-- | src/libstd/sys/windows/time.rs | 50 |
5 files changed, 212 insertions, 20 deletions
diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index 52dd261824f..85a65bbef50 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -10,9 +10,12 @@ use cell::UnsafeCell; use libc; +use std::option::Option::{Some, None}; use sys::mutex::{self, Mutex}; +use sys::time; use sys::sync as ffi; use time::Duration; +use num::{Int, NumCast}; pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> } @@ -46,33 +49,46 @@ impl Condvar { debug_assert_eq!(r, 0); } + // This implementation is modeled after libcxx's condition_variable + // 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 { - assert!(dur >= Duration::nanoseconds(0)); + if dur <= Duration::zero() { + return false; + } - // First, figure out what time it currently is - let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 }; - let r = ffi::gettimeofday(&mut tv, 0 as *mut _); + // 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, 0 as *mut _); debug_assert_eq!(r, 0); - // Offset that time with the specified duration - let abs = Duration::seconds(tv.tv_sec as i64) + - Duration::microseconds(tv.tv_usec as i64) + - dur; - let ns = abs.num_nanoseconds().unwrap() as u64; - let timeout = libc::timespec { - tv_sec: (ns / 1000000000) as libc::time_t, - tv_nsec: (ns % 1000000000) as libc::c_long, + let seconds = NumCast::from(dur.num_seconds()); + let timeout = match seconds.and_then(|s| sys_now.tv_sec.checked_add(s)) { + Some(sec) => { + libc::timespec { + tv_sec: sec, + tv_nsec: (dur - Duration::seconds(dur.num_seconds())) + .num_nanoseconds().unwrap() as libc::c_long, + } + } + None => { + libc::timespec { + tv_sec: Int::max_value(), + tv_nsec: 1_000_000_000 - 1, + } + } }; // And wait! - let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), - &timeout); - if r != 0 { - debug_assert_eq!(r as int, libc::ETIMEDOUT as int); - false - } else { - true - } + 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 + &time::SteadyTime::now() - &stable_now < dur } #[inline] diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 6a408aa60f0..bb98d1e052a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -52,6 +52,7 @@ pub mod sync; pub mod tcp; pub mod thread; pub mod thread_local; +pub mod time; pub mod timer; pub mod tty; pub mod udp; diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs new file mode 100644 index 00000000000..cc1e23fbca9 --- /dev/null +++ b/src/libstd/sys/unix/time.rs @@ -0,0 +1,124 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::inner::SteadyTime; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod inner { + use libc; + use time::Duration; + use ops::Sub; + use sync::{Once, ONCE_INIT}; + + pub struct SteadyTime { + t: u64 + } + + extern { + pub fn mach_absolute_time() -> u64; + pub fn mach_timebase_info(info: *mut libc::mach_timebase_info) -> libc::c_int; + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + SteadyTime { + 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 { + static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info { + numer: 0, + denom: 0, + }; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + mach_timebase_info(&mut INFO); + }); + &INFO + } + } + + impl<'a> Sub for &'a SteadyTime { + type Output = Duration; + + fn sub(self, other: &SteadyTime) -> Duration { + unsafe { + let info = info(); + let diff = self.t as i64 - other.t as i64; + Duration::nanoseconds(diff * info.numer as i64 / info.denom as i64) + } + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod inner { + use libc; + use time::Duration; + use ops::Sub; + + const NSEC_PER_SEC: i64 = 1_000_000_000; + + pub struct SteadyTime { + t: libc::timespec, + } + + // Apparently android provides this in some other library? + #[cfg(not(target_os = "android"))] + #[link(name = "rt")] + extern {} + + extern { + fn clock_gettime(clk_id: libc::c_int, tp: *mut libc::timespec) -> libc::c_int; + } + + impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + } + }; + unsafe { + assert_eq!(0, clock_gettime(libc::CLOCK_MONOTONIC, &mut t.t)); + } + 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 { + type Output = Duration; + + 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) + } 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) + } + } + } +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 0e706c3cc6a..72fc2f8700d 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -50,6 +50,7 @@ pub mod rwlock; pub mod sync; pub mod stack_overflow; pub mod tcp; +pub mod time; pub mod thread; pub mod thread_local; pub mod timer; diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs new file mode 100644 index 00000000000..20ceff0aa69 --- /dev/null +++ b/src/libstd/sys/windows/time.rs @@ -0,0 +1,50 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use libc; +use ops::Sub; +use time::Duration; +use sync::{Once, ONCE_INIT}; + +pub struct SteadyTime { + t: libc::LARGE_INTEGER, +} + +impl SteadyTime { + pub fn now() -> SteadyTime { + let mut t = SteadyTime { t: 0 }; + unsafe { libc::QueryPerformanceCounter(&mut t.t); } + t + } + + pub fn ns(&self) -> u64 { + self.t as u64 * 1_000_000_000 / frequency() as u64 + } +} + +fn frequency() -> libc::LARGE_INTEGER { + static mut FREQUENCY: libc::LARGE_INTEGER = 0; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + libc::QueryPerformanceFrequency(&mut FREQUENCY); + }); + FREQUENCY + } +} + +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::microseconds(diff * 1_000_000 / frequency() as i64) + } +} |
