1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use std::option::Option::{Some, None};
15 use sys::mutex::{self, Mutex};
19 use num::{Int, NumCast};
21 pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> }
23 pub const CONDVAR_INIT: Condvar = Condvar {
24 inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER },
29 pub unsafe fn new() -> Condvar {
30 // Might be moved and address is changing it is better to avoid
31 // initialization of potentially opaque OS data before it landed
32 Condvar { inner: UnsafeCell::new(ffi::PTHREAD_COND_INITIALIZER) }
36 pub unsafe fn notify_one(&self) {
37 let r = ffi::pthread_cond_signal(self.inner.get());
38 debug_assert_eq!(r, 0);
42 pub unsafe fn notify_all(&self) {
43 let r = ffi::pthread_cond_broadcast(self.inner.get());
44 debug_assert_eq!(r, 0);
48 pub unsafe fn wait(&self, mutex: &Mutex) {
49 let r = ffi::pthread_cond_wait(self.inner.get(), mutex::raw(mutex));
50 debug_assert_eq!(r, 0);
53 // This implementation is modeled after libcxx's condition_variable
54 // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
55 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
56 pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
57 if dur <= Duration::zero() {
61 // First, figure out what time it currently is, in both system and stable time.
62 // pthread_cond_timedwait uses system time, but we want to report timeout based on stable
64 let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
65 let stable_now = time::SteadyTime::now();
66 let r = ffi::gettimeofday(&mut sys_now, ptr::null_mut());
67 debug_assert_eq!(r, 0);
69 let seconds = NumCast::from(dur.num_seconds());
70 let timeout = match seconds.and_then(|s| sys_now.tv_sec.checked_add(s)) {
74 tv_nsec: (dur - Duration::seconds(dur.num_seconds()))
75 .num_nanoseconds().unwrap() as libc::c_long,
80 tv_sec: Int::max_value(),
81 tv_nsec: 1_000_000_000 - 1,
87 let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout);
88 debug_assert!(r == libc::ETIMEDOUT || r == 0);
90 // ETIMEDOUT is not a totally reliable method of determining timeout due to clock shifts,
91 // so do the check ourselves
92 &time::SteadyTime::now() - &stable_now < dur
96 #[cfg(not(target_os = "dragonfly"))]
97 pub unsafe fn destroy(&self) {
98 let r = ffi::pthread_cond_destroy(self.inner.get());
99 debug_assert_eq!(r, 0);
103 #[cfg(target_os = "dragonfly")]
104 pub unsafe fn destroy(&self) {
105 let r = ffi::pthread_cond_destroy(self.inner.get());
106 // On DragonFly pthread_cond_destroy() returns EINVAL if called on
107 // a condvar that was just initialized with
108 // ffi::PTHREAD_COND_INITIALIZER. Once it is used or
109 // pthread_cond_init() is called, this behaviour no longer occurs.
110 debug_assert!(r == 0 || r == libc::EINVAL);