]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/condvar.rs
rollup merge of #20642: michaelwoerister/sane-source-locations-pt1
[rust.git] / src / libstd / sys / unix / condvar.rs
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.
4 //
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.
10
11 use cell::UnsafeCell;
12 use libc;
13 use ptr;
14 use std::option::Option::{Some, None};
15 use sys::mutex::{self, Mutex};
16 use sys::time;
17 use sys::sync as ffi;
18 use time::Duration;
19 use num::{Int, NumCast};
20
21 pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> }
22
23 pub const CONDVAR_INIT: Condvar = Condvar {
24     inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER },
25 };
26
27 impl Condvar {
28     #[inline]
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) }
33     }
34
35     #[inline]
36     pub unsafe fn notify_one(&self) {
37         let r = ffi::pthread_cond_signal(self.inner.get());
38         debug_assert_eq!(r, 0);
39     }
40
41     #[inline]
42     pub unsafe fn notify_all(&self) {
43         let r = ffi::pthread_cond_broadcast(self.inner.get());
44         debug_assert_eq!(r, 0);
45     }
46
47     #[inline]
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);
51     }
52
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() {
58             return false;
59         }
60
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
63         // time.
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);
68
69         let seconds = NumCast::from(dur.num_seconds());
70         let timeout = match seconds.and_then(|s| sys_now.tv_sec.checked_add(s)) {
71             Some(sec) => {
72                 libc::timespec {
73                     tv_sec: sec,
74                     tv_nsec: (dur - Duration::seconds(dur.num_seconds()))
75                         .num_nanoseconds().unwrap() as libc::c_long,
76                 }
77             }
78             None => {
79                 libc::timespec {
80                     tv_sec: Int::max_value(),
81                     tv_nsec: 1_000_000_000 - 1,
82                 }
83             }
84         };
85
86         // And wait!
87         let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout);
88         debug_assert!(r == libc::ETIMEDOUT || r == 0);
89
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
93     }
94
95     #[inline]
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);
100     }
101
102     #[inline]
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);
111     }
112 }