about summary refs log tree commit diff
path: root/src/libstd/sys/unix/condvar.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/unix/condvar.rs')
-rw-r--r--src/libstd/sys/unix/condvar.rs67
1 files changed, 59 insertions, 8 deletions
diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs
index 2e1c1900b46..725a071a4f9 100644
--- a/src/libstd/sys/unix/condvar.rs
+++ b/src/libstd/sys/unix/condvar.rs
@@ -10,15 +10,19 @@
 
 use cell::UnsafeCell;
 use libc;
-use ptr;
 use sys::mutex::{self, Mutex};
-use time::{Instant, Duration};
+use time::Duration;
 
 pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
 
 unsafe impl Send for Condvar {}
 unsafe impl Sync for Condvar {}
 
+const TIMESPEC_MAX: libc::timespec = libc::timespec {
+    tv_sec: <libc::time_t>::max_value(),
+    tv_nsec: 1_000_000_000 - 1,
+};
+
 impl Condvar {
     pub const fn new() -> Condvar {
         // Might be moved and address is changing it is better to avoid
@@ -26,6 +30,23 @@ impl Condvar {
         Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
     }
 
+    #[cfg(any(target_os = "macos", target_os = "ios"))]
+    pub unsafe fn init(&mut self) {}
+
+    #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+    pub unsafe fn init(&mut self) {
+        use mem;
+        let mut attr: libc::pthread_condattr_t = mem::uninitialized();
+        let r = libc::pthread_condattr_init(&mut attr);
+        assert_eq!(r, 0);
+        let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
+        assert_eq!(r, 0);
+        let r = libc::pthread_cond_init(self.inner.get(), &attr);
+        assert_eq!(r, 0);
+        let r = libc::pthread_condattr_destroy(&mut attr);
+        assert_eq!(r, 0);
+    }
+
     #[inline]
     pub unsafe fn notify_one(&self) {
         let r = libc::pthread_cond_signal(self.inner.get());
@@ -44,10 +65,45 @@ impl Condvar {
         debug_assert_eq!(r, 0);
     }
 
+    // This implementation is used on systems that support pthread_condattr_setclock
+    // where we configure condition variable to use monotonic clock (instead of
+    // default system clock). This approach avoids all problems that result
+    // from changes made to the system time.
+    #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        use mem;
+
+        let mut now: libc::timespec = mem::zeroed();
+        let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
+        assert_eq!(r, 0);
+
+        // Nanosecond calculations can't overflow because both values are below 1e9.
+        let nsec = dur.subsec_nanos() as libc::c_long + now.tv_nsec as libc::c_long;
+        // FIXME: Casting u64 into time_t could truncate the value.
+        let sec = (dur.as_secs() as libc::time_t)
+            .checked_add((nsec / 1_000_000_000) as libc::time_t)
+            .and_then(|s| s.checked_add(now.tv_sec));
+        let nsec = nsec % 1_000_000_000;
+
+        let timeout = sec.map(|s| {
+            libc::timespec { tv_sec: s, tv_nsec: nsec }
+        }).unwrap_or(TIMESPEC_MAX);
+
+        let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
+                                            &timeout);
+        assert!(r == libc::ETIMEDOUT || r == 0);
+        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
+    #[cfg(any(target_os = "macos", target_os = "ios"))]
     pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        use ptr;
+        use time::Instant;
+
         // 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.
@@ -66,12 +122,7 @@ impl Condvar {
             s.checked_add(seconds)
         }).map(|s| {
             libc::timespec { tv_sec: s, tv_nsec: nsec }
-        }).unwrap_or_else(|| {
-            libc::timespec {
-                tv_sec: <libc::time_t>::max_value(),
-                tv_nsec: 1_000_000_000 - 1,
-            }
-        });
+        }).unwrap_or(TIMESPEC_MAX);
 
         // And wait!
         let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),