]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
c34b1796 AL |
11 | use prelude::v1::*; |
12 | ||
1a4d82fc JJ |
13 | use cell::UnsafeCell; |
14 | use libc; | |
85aaf69f | 15 | use ptr; |
1a4d82fc | 16 | use sys::mutex::{self, Mutex}; |
85aaf69f | 17 | use sys::time; |
1a4d82fc JJ |
18 | use sys::sync as ffi; |
19 | use time::Duration; | |
85aaf69f | 20 | use num::{Int, NumCast}; |
1a4d82fc JJ |
21 | |
22 | pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> } | |
23 | ||
c34b1796 AL |
24 | unsafe impl Send for Condvar {} |
25 | unsafe impl Sync for Condvar {} | |
26 | ||
1a4d82fc JJ |
27 | pub const CONDVAR_INIT: Condvar = Condvar { |
28 | inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER }, | |
29 | }; | |
30 | ||
31 | impl Condvar { | |
32 | #[inline] | |
33 | pub unsafe fn new() -> Condvar { | |
34 | // Might be moved and address is changing it is better to avoid | |
35 | // initialization of potentially opaque OS data before it landed | |
36 | Condvar { inner: UnsafeCell::new(ffi::PTHREAD_COND_INITIALIZER) } | |
37 | } | |
38 | ||
39 | #[inline] | |
40 | pub unsafe fn notify_one(&self) { | |
41 | let r = ffi::pthread_cond_signal(self.inner.get()); | |
42 | debug_assert_eq!(r, 0); | |
43 | } | |
44 | ||
45 | #[inline] | |
46 | pub unsafe fn notify_all(&self) { | |
47 | let r = ffi::pthread_cond_broadcast(self.inner.get()); | |
48 | debug_assert_eq!(r, 0); | |
49 | } | |
50 | ||
51 | #[inline] | |
52 | pub unsafe fn wait(&self, mutex: &Mutex) { | |
53 | let r = ffi::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); | |
54 | debug_assert_eq!(r, 0); | |
55 | } | |
56 | ||
85aaf69f SL |
57 | // This implementation is modeled after libcxx's condition_variable |
58 | // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 | |
59 | // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 | |
1a4d82fc | 60 | pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { |
85aaf69f SL |
61 | if dur <= Duration::zero() { |
62 | return false; | |
63 | } | |
1a4d82fc | 64 | |
85aaf69f SL |
65 | // First, figure out what time it currently is, in both system and stable time. |
66 | // pthread_cond_timedwait uses system time, but we want to report timeout based on stable | |
67 | // time. | |
68 | let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 }; | |
69 | let stable_now = time::SteadyTime::now(); | |
70 | let r = ffi::gettimeofday(&mut sys_now, ptr::null_mut()); | |
1a4d82fc JJ |
71 | debug_assert_eq!(r, 0); |
72 | ||
85aaf69f SL |
73 | let seconds = NumCast::from(dur.num_seconds()); |
74 | let timeout = match seconds.and_then(|s| sys_now.tv_sec.checked_add(s)) { | |
75 | Some(sec) => { | |
76 | libc::timespec { | |
77 | tv_sec: sec, | |
78 | tv_nsec: (dur - Duration::seconds(dur.num_seconds())) | |
79 | .num_nanoseconds().unwrap() as libc::c_long, | |
80 | } | |
81 | } | |
82 | None => { | |
83 | libc::timespec { | |
84 | tv_sec: Int::max_value(), | |
85 | tv_nsec: 1_000_000_000 - 1, | |
86 | } | |
87 | } | |
1a4d82fc JJ |
88 | }; |
89 | ||
90 | // And wait! | |
85aaf69f SL |
91 | let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); |
92 | debug_assert!(r == libc::ETIMEDOUT || r == 0); | |
93 | ||
94 | // ETIMEDOUT is not a totally reliable method of determining timeout due to clock shifts, | |
95 | // so do the check ourselves | |
96 | &time::SteadyTime::now() - &stable_now < dur | |
1a4d82fc JJ |
97 | } |
98 | ||
99 | #[inline] | |
85aaf69f | 100 | #[cfg(not(target_os = "dragonfly"))] |
1a4d82fc JJ |
101 | pub unsafe fn destroy(&self) { |
102 | let r = ffi::pthread_cond_destroy(self.inner.get()); | |
103 | debug_assert_eq!(r, 0); | |
104 | } | |
85aaf69f SL |
105 | |
106 | #[inline] | |
107 | #[cfg(target_os = "dragonfly")] | |
108 | pub unsafe fn destroy(&self) { | |
109 | let r = ffi::pthread_cond_destroy(self.inner.get()); | |
110 | // On DragonFly pthread_cond_destroy() returns EINVAL if called on | |
111 | // a condvar that was just initialized with | |
112 | // ffi::PTHREAD_COND_INITIALIZER. Once it is used or | |
113 | // pthread_cond_init() is called, this behaviour no longer occurs. | |
114 | debug_assert!(r == 0 || r == libc::EINVAL); | |
115 | } | |
1a4d82fc | 116 | } |