]> git.proxmox.com Git - rustc.git/blame - src/libstd/sync/condvar.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / libstd / sync / condvar.rs
CommitLineData
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
62682a34 11use sync::atomic::{AtomicUsize, Ordering};
9346a6ac 12use sync::{mutex, MutexGuard, PoisonError};
1a4d82fc
JJ
13use sys_common::condvar as sys;
14use sys_common::mutex as sys_mutex;
9346a6ac 15use sys_common::poison::{self, LockResult};
5bcae85e 16use time::Duration;
1a4d82fc 17
e9174d1e
SL
18/// A type indicating whether a timed wait on a condition variable returned
19/// due to a time out or not.
20#[derive(Debug, PartialEq, Eq, Copy, Clone)]
b039eaaf 21#[stable(feature = "wait_timeout", since = "1.5.0")]
e9174d1e
SL
22pub struct WaitTimeoutResult(bool);
23
24impl WaitTimeoutResult {
25 /// Returns whether the wait was known to have timed out.
b039eaaf 26 #[stable(feature = "wait_timeout", since = "1.5.0")]
e9174d1e
SL
27 pub fn timed_out(&self) -> bool {
28 self.0
29 }
30}
31
1a4d82fc
JJ
32/// A Condition Variable
33///
34/// Condition variables represent the ability to block a thread such that it
35/// consumes no CPU time while waiting for an event to occur. Condition
36/// variables are typically associated with a boolean predicate (a condition)
37/// and a mutex. The predicate is always verified inside of the mutex before
38/// determining that thread must block.
39///
40/// Functions in this module will block the current **thread** of execution and
41/// are bindings to system-provided condition variables where possible. Note
42/// that this module places one additional restriction over the system condition
43/// variables: each condvar can be used with precisely one mutex at runtime. Any
44/// attempt to use multiple mutexes on the same condition variable will result
45/// in a runtime panic. If this is not desired, then the unsafe primitives in
46/// `sys` do not have this restriction but may result in undefined behavior.
47///
c34b1796 48/// # Examples
1a4d82fc
JJ
49///
50/// ```
51/// use std::sync::{Arc, Mutex, Condvar};
85aaf69f 52/// use std::thread;
1a4d82fc
JJ
53///
54/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
55/// let pair2 = pair.clone();
56///
57/// // Inside of our lock, spawn a new thread, and then wait for it to start
85aaf69f 58/// thread::spawn(move|| {
1a4d82fc
JJ
59/// let &(ref lock, ref cvar) = &*pair2;
60/// let mut started = lock.lock().unwrap();
61/// *started = true;
62/// cvar.notify_one();
63/// });
64///
65/// // wait for the thread to start up
66/// let &(ref lock, ref cvar) = &*pair;
67/// let mut started = lock.lock().unwrap();
68/// while !*started {
69/// started = cvar.wait(started).unwrap();
70/// }
71/// ```
85aaf69f 72#[stable(feature = "rust1", since = "1.0.0")]
5bcae85e
SL
73pub struct Condvar {
74 inner: Box<sys::Condvar>,
85aaf69f 75 mutex: AtomicUsize,
1a4d82fc
JJ
76}
77
1a4d82fc
JJ
78impl Condvar {
79 /// Creates a new condition variable which is ready to be waited on and
80 /// notified.
85aaf69f 81 #[stable(feature = "rust1", since = "1.0.0")]
1a4d82fc 82 pub fn new() -> Condvar {
9e0c209e 83 let mut c = Condvar {
5bcae85e
SL
84 inner: box sys::Condvar::new(),
85 mutex: AtomicUsize::new(0),
9e0c209e
SL
86 };
87 unsafe {
88 c.inner.init();
1a4d82fc 89 }
9e0c209e 90 c
1a4d82fc
JJ
91 }
92
9346a6ac 93 /// Blocks the current thread until this condition variable receives a
1a4d82fc
JJ
94 /// notification.
95 ///
96 /// This function will atomically unlock the mutex specified (represented by
97 /// `mutex_guard`) and block the current thread. This means that any calls
98 /// to `notify_*()` which happen logically after the mutex is unlocked are
99 /// candidates to wake this thread up. When this function call returns, the
100 /// lock specified will have been re-acquired.
101 ///
102 /// Note that this function is susceptible to spurious wakeups. Condition
103 /// variables normally have a boolean predicate associated with them, and
104 /// the predicate must always be checked each time this function returns to
105 /// protect against spurious wakeups.
106 ///
7453a54e 107 /// # Errors
1a4d82fc
JJ
108 ///
109 /// This function will return an error if the mutex being waited on is
110 /// poisoned when this thread re-acquires the lock. For more information,
111 /// see information about poisoning on the Mutex type.
112 ///
113 /// # Panics
114 ///
115 /// This function will `panic!()` if it is used with more than one mutex
116 /// over time. Each condition variable is dynamically bound to exactly one
117 /// mutex to ensure defined behavior across platforms. If this functionality
118 /// is not desired, then unsafe primitives in `sys` are provided.
85aaf69f 119 #[stable(feature = "rust1", since = "1.0.0")]
1a4d82fc
JJ
120 pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>)
121 -> LockResult<MutexGuard<'a, T>> {
5bcae85e
SL
122 let poisoned = unsafe {
123 let lock = mutex::guard_lock(&guard);
124 self.verify(lock);
125 self.inner.wait(lock);
126 mutex::guard_poison(&guard).get()
127 };
128 if poisoned {
129 Err(PoisonError::new(guard))
130 } else {
131 Ok(guard)
1a4d82fc
JJ
132 }
133 }
134
9346a6ac 135 /// Waits on this condition variable for a notification, timing out after a
1a4d82fc
JJ
136 /// specified duration.
137 ///
c34b1796
AL
138 /// The semantics of this function are equivalent to `wait()`
139 /// except that the thread will be blocked for roughly no longer
140 /// than `ms` milliseconds. This method should not be used for
141 /// precise timing due to anomalies such as preemption or platform
142 /// differences that may not cause the maximum amount of time
143 /// waited to be precisely `ms`.
1a4d82fc 144 ///
9e0c209e
SL
145 /// Note that the best effort is made to ensure that the time waited is
146 /// measured with a monotonic clock, and not affected by the changes made to
147 /// the system time.
148 ///
c34b1796
AL
149 /// The returned boolean is `false` only if the timeout is known
150 /// to have elapsed.
1a4d82fc
JJ
151 ///
152 /// Like `wait`, the lock specified will be re-acquired when this function
153 /// returns, regardless of whether the timeout elapsed or not.
c34b1796 154 #[stable(feature = "rust1", since = "1.0.0")]
92a42be0 155 #[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::sync::Condvar::wait_timeout`")]
c34b1796
AL
156 pub fn wait_timeout_ms<'a, T>(&self, guard: MutexGuard<'a, T>, ms: u32)
157 -> LockResult<(MutexGuard<'a, T>, bool)> {
54a0048b
SL
158 let res = self.wait_timeout(guard, Duration::from_millis(ms as u64));
159 poison::map_result(res, |(a, b)| {
160 (a, !b.timed_out())
161 })
1a4d82fc
JJ
162 }
163
d9579d0f
AL
164 /// Waits on this condition variable for a notification, timing out after a
165 /// specified duration.
166 ///
167 /// The semantics of this function are equivalent to `wait()` except that
168 /// the thread will be blocked for roughly no longer than `dur`. This
169 /// method should not be used for precise timing due to anomalies such as
170 /// preemption or platform differences that may not cause the maximum
171 /// amount of time waited to be precisely `dur`.
172 ///
9e0c209e
SL
173 /// Note that the best effort is made to ensure that the time waited is
174 /// measured with a monotonic clock, and not affected by the changes made to
175 /// the system time.
176 ///
e9174d1e
SL
177 /// The returned `WaitTimeoutResult` value indicates if the timeout is
178 /// known to have elapsed.
d9579d0f
AL
179 ///
180 /// Like `wait`, the lock specified will be re-acquired when this function
181 /// returns, regardless of whether the timeout elapsed or not.
b039eaaf 182 #[stable(feature = "wait_timeout", since = "1.5.0")]
d9579d0f
AL
183 pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>,
184 dur: Duration)
e9174d1e 185 -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
5bcae85e
SL
186 let (poisoned, result) = unsafe {
187 let lock = mutex::guard_lock(&guard);
188 self.verify(lock);
189 let success = self.inner.wait_timeout(lock, dur);
190 (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
191 };
192 if poisoned {
193 Err(PoisonError::new((guard, result)))
194 } else {
195 Ok((guard, result))
d9579d0f
AL
196 }
197 }
198
9346a6ac 199 /// Wakes up one blocked thread on this condvar.
1a4d82fc
JJ
200 ///
201 /// If there is a blocked thread on this condition variable, then it will
202 /// be woken up from its call to `wait` or `wait_timeout`. Calls to
203 /// `notify_one` are not buffered in any way.
204 ///
85aaf69f
SL
205 /// To wake up all threads, see `notify_all()`.
206 #[stable(feature = "rust1", since = "1.0.0")]
5bcae85e
SL
207 pub fn notify_one(&self) {
208 unsafe { self.inner.notify_one() }
209 }
1a4d82fc 210
9346a6ac 211 /// Wakes up all blocked threads on this condvar.
1a4d82fc
JJ
212 ///
213 /// This method will ensure that any current waiters on the condition
214 /// variable are awoken. Calls to `notify_all()` are not buffered in any
215 /// way.
216 ///
217 /// To wake up only one thread, see `notify_one()`.
85aaf69f 218 #[stable(feature = "rust1", since = "1.0.0")]
5bcae85e
SL
219 pub fn notify_all(&self) {
220 unsafe { self.inner.notify_all() }
1a4d82fc
JJ
221 }
222
223 fn verify(&self, mutex: &sys_mutex::Mutex) {
c34b1796 224 let addr = mutex as *const _ as usize;
1a4d82fc
JJ
225 match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) {
226 // If we got out 0, then we have successfully bound the mutex to
227 // this cvar.
228 0 => {}
229
230 // If we get out a value that's the same as `addr`, then someone
231 // already beat us to the punch.
232 n if n == addr => {}
233
234 // Anything else and we're using more than one mutex on this cvar,
235 // which is currently disallowed.
236 _ => panic!("attempted to use a condition variable with two \
237 mutexes"),
238 }
239 }
240}
241
5bcae85e
SL
242#[stable(feature = "condvar_default", since = "1.9.0")]
243impl Default for Condvar {
9e0c209e 244 /// Creates a `Condvar` which is ready to be waited on and notified.
5bcae85e
SL
245 fn default() -> Condvar {
246 Condvar::new()
247 }
248}
249
250#[stable(feature = "rust1", since = "1.0.0")]
251impl Drop for Condvar {
252 fn drop(&mut self) {
253 unsafe { self.inner.destroy() }
254 }
255}
256
1a4d82fc
JJ
257#[cfg(test)]
258mod tests {
1a4d82fc 259 use sync::mpsc::channel;
5bcae85e 260 use sync::{Condvar, Mutex, Arc};
85aaf69f 261 use thread;
1a4d82fc 262 use time::Duration;
c34b1796 263 use u32;
1a4d82fc
JJ
264
265 #[test]
266 fn smoke() {
267 let c = Condvar::new();
268 c.notify_one();
269 c.notify_all();
270 }
271
1a4d82fc 272 #[test]
c30ab7b3 273 #[cfg_attr(target_os = "emscripten", ignore)]
1a4d82fc 274 fn notify_one() {
5bcae85e
SL
275 let m = Arc::new(Mutex::new(()));
276 let m2 = m.clone();
277 let c = Arc::new(Condvar::new());
278 let c2 = c.clone();
1a4d82fc 279
5bcae85e 280 let g = m.lock().unwrap();
85aaf69f 281 let _t = thread::spawn(move|| {
5bcae85e
SL
282 let _g = m2.lock().unwrap();
283 c2.notify_one();
1a4d82fc 284 });
5bcae85e 285 let g = c.wait(g).unwrap();
1a4d82fc 286 drop(g);
1a4d82fc
JJ
287 }
288
289 #[test]
c30ab7b3 290 #[cfg_attr(target_os = "emscripten", ignore)]
1a4d82fc 291 fn notify_all() {
c34b1796 292 const N: usize = 10;
1a4d82fc
JJ
293
294 let data = Arc::new((Mutex::new(0), Condvar::new()));
295 let (tx, rx) = channel();
85aaf69f 296 for _ in 0..N {
1a4d82fc
JJ
297 let data = data.clone();
298 let tx = tx.clone();
85aaf69f 299 thread::spawn(move|| {
1a4d82fc
JJ
300 let &(ref lock, ref cond) = &*data;
301 let mut cnt = lock.lock().unwrap();
302 *cnt += 1;
303 if *cnt == N {
304 tx.send(()).unwrap();
305 }
306 while *cnt != 0 {
307 cnt = cond.wait(cnt).unwrap();
308 }
309 tx.send(()).unwrap();
310 });
311 }
312 drop(tx);
313
314 let &(ref lock, ref cond) = &*data;
315 rx.recv().unwrap();
316 let mut cnt = lock.lock().unwrap();
317 *cnt = 0;
318 cond.notify_all();
319 drop(cnt);
320
85aaf69f 321 for _ in 0..N {
1a4d82fc
JJ
322 rx.recv().unwrap();
323 }
324 }
325
326 #[test]
c30ab7b3 327 #[cfg_attr(target_os = "emscripten", ignore)]
c34b1796 328 fn wait_timeout_ms() {
5bcae85e
SL
329 let m = Arc::new(Mutex::new(()));
330 let m2 = m.clone();
331 let c = Arc::new(Condvar::new());
332 let c2 = c.clone();
1a4d82fc 333
5bcae85e
SL
334 let g = m.lock().unwrap();
335 let (g, _no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
85aaf69f
SL
336 // spurious wakeups mean this isn't necessarily true
337 // assert!(!no_timeout);
338 let _t = thread::spawn(move || {
5bcae85e
SL
339 let _g = m2.lock().unwrap();
340 c2.notify_one();
1a4d82fc 341 });
5bcae85e 342 let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap();
9cc50fc6 343 assert!(!timeout_res.timed_out());
1a4d82fc 344 drop(g);
85aaf69f
SL
345 }
346
1a4d82fc 347 #[test]
c34b1796 348 #[should_panic]
c30ab7b3 349 #[cfg_attr(target_os = "emscripten", ignore)]
1a4d82fc 350 fn two_mutexes() {
5bcae85e
SL
351 let m = Arc::new(Mutex::new(()));
352 let m2 = m.clone();
353 let c = Arc::new(Condvar::new());
354 let c2 = c.clone();
1a4d82fc 355
5bcae85e 356 let mut g = m.lock().unwrap();
85aaf69f 357 let _t = thread::spawn(move|| {
5bcae85e
SL
358 let _g = m2.lock().unwrap();
359 c2.notify_one();
1a4d82fc 360 });
5bcae85e 361 g = c.wait(g).unwrap();
1a4d82fc
JJ
362 drop(g);
363
5bcae85e
SL
364 let m = Mutex::new(());
365 let _ = c.wait(m.lock().unwrap()).unwrap();
1a4d82fc
JJ
366 }
367}