10
10
11
11
use cell::UnsafeCell;
12
12
use libc;
13
- use ptr;
14
13
use sys::mutex::{self, Mutex};
15
- use time::{Instant, Duration} ;
14
+ use time::Duration;
16
15
17
16
pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
18
17
19
18
unsafe impl Send for Condvar {}
20
19
unsafe impl Sync for Condvar {}
21
20
21
+ const TIMESPEC_MAX: libc::timespec = libc::timespec {
22
+ tv_sec: <libc::time_t>::max_value(),
23
+ tv_nsec: 1_000_000_000 - 1,
24
+ };
25
+
22
26
impl Condvar {
23
27
pub const fn new() -> Condvar {
24
28
// Might be moved and address is changing it is better to avoid
25
29
// initialization of potentially opaque OS data before it landed
26
30
Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
27
31
}
28
32
33
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
34
+ pub unsafe fn init(&mut self) {}
35
+
36
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
37
+ pub unsafe fn init(&mut self) {
38
+ use mem;
39
+ let mut attr: libc::pthread_condattr_t = mem::uninitialized();
40
+ let r = libc::pthread_condattr_init(&mut attr);
41
+ assert_eq!(r, 0);
42
+ let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
43
+ assert_eq!(r, 0);
44
+ let r = libc::pthread_cond_init(self.inner.get(), &attr);
45
+ assert_eq!(r, 0);
46
+ let r = libc::pthread_condattr_destroy(&mut attr);
47
+ assert_eq!(r, 0);
48
+ }
49
+
29
50
#[inline]
30
51
pub unsafe fn notify_one(&self) {
31
52
let r = libc::pthread_cond_signal(self.inner.get());
@@ -44,10 +65,45 @@ impl Condvar {
44
65
debug_assert_eq!(r, 0);
45
66
}
46
67
68
+ // This implementation is used on systems that support pthread_condattr_setclock
69
+ // where we configure condition variable to use monotonic clock (instead of
70
+ // default system clock). This approach avoids all problems that result
71
+ // from changes made to the system time.
72
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
73
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
74
+ use mem;
75
+
76
+ let mut now: libc::timespec = mem::zeroed();
77
+ let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
78
+ assert_eq!(r, 0);
79
+
80
+ // Nanosecond calculations can't overflow because both values are below 1e9.
81
+ let nsec = dur.subsec_nanos() as libc::c_long + now.tv_nsec as libc::c_long;
82
+ // FIXME: Casting u64 into time_t could truncate the value.
83
+ let sec = (dur.as_secs() as libc::time_t)
84
+ .checked_add((nsec / 1_000_000_000) as libc::time_t)
85
+ .and_then(|s| s.checked_add(now.tv_sec));
86
+ let nsec = nsec % 1_000_000_000;
87
+
88
+ let timeout = sec.map(|s| {
89
+ libc::timespec { tv_sec: s, tv_nsec: nsec }
90
+ }).unwrap_or(TIMESPEC_MAX);
91
+
92
+ let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
93
+ &timeout);
94
+ assert!(r == libc::ETIMEDOUT || r == 0);
95
+ r == 0
96
+ }
97
+
98
+
47
99
// This implementation is modeled after libcxx's condition_variable
48
100
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
49
101
// https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
102
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
50
103
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
104
+ use ptr;
105
+ use time::Instant;
106
+
51
107
// First, figure out what time it currently is, in both system and
52
108
// stable time. pthread_cond_timedwait uses system time, but we want to
53
109
// report timeout based on stable time.
@@ -66,12 +122,7 @@ impl Condvar {
66
122
s.checked_add(seconds)
67
123
}).map(|s| {
68
124
libc::timespec { tv_sec: s, tv_nsec: nsec }
69
- }).unwrap_or_else(|| {
70
- libc::timespec {
71
- tv_sec: <libc::time_t>::max_value(),
72
- tv_nsec: 1_000_000_000 - 1,
73
- }
74
- });
125
+ }).unwrap_or(TIMESPEC_MAX);
75
126
76
127
// And wait!
77
128
let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
0 commit comments