]>
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 | ||
62682a34 | 11 | use sync::atomic::{AtomicUsize, Ordering}; |
9346a6ac | 12 | use sync::{mutex, MutexGuard, PoisonError}; |
1a4d82fc JJ |
13 | use sys_common::condvar as sys; |
14 | use sys_common::mutex as sys_mutex; | |
9346a6ac | 15 | use sys_common::poison::{self, LockResult}; |
5bcae85e | 16 | use 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 |
22 | pub struct WaitTimeoutResult(bool); |
23 | ||
24 | impl 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 |
73 | pub struct Condvar { |
74 | inner: Box<sys::Condvar>, | |
85aaf69f | 75 | mutex: AtomicUsize, |
1a4d82fc JJ |
76 | } |
77 | ||
1a4d82fc JJ |
78 | impl 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")] |
243 | impl 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")] | |
251 | impl Drop for Condvar { | |
252 | fn drop(&mut self) { | |
253 | unsafe { self.inner.destroy() } | |
254 | } | |
255 | } | |
256 | ||
1a4d82fc JJ |
257 | #[cfg(test)] |
258 | mod 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 | } |