about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/condvar.rs56
-rw-r--r--src/libstd/sys/unix/mod.rs1
-rw-r--r--src/libstd/sys/unix/time.rs124
-rw-r--r--src/libstd/sys/windows/mod.rs1
-rw-r--r--src/libstd/sys/windows/time.rs50
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)
+    }
+}