]>
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 | ||
11 | use prelude::v1::*; | |
12 | ||
62682a34 | 13 | use sync::atomic::{AtomicUsize, Ordering}; |
9346a6ac | 14 | use sync::{mutex, MutexGuard, PoisonError}; |
1a4d82fc JJ |
15 | use sys_common::condvar as sys; |
16 | use sys_common::mutex as sys_mutex; | |
9346a6ac AL |
17 | use sys_common::poison::{self, LockResult}; |
18 | use sys::time::SteadyTime; | |
1a4d82fc | 19 | use time::Duration; |
1a4d82fc JJ |
20 | |
21 | /// A Condition Variable | |
22 | /// | |
23 | /// Condition variables represent the ability to block a thread such that it | |
24 | /// consumes no CPU time while waiting for an event to occur. Condition | |
25 | /// variables are typically associated with a boolean predicate (a condition) | |
26 | /// and a mutex. The predicate is always verified inside of the mutex before | |
27 | /// determining that thread must block. | |
28 | /// | |
29 | /// Functions in this module will block the current **thread** of execution and | |
30 | /// are bindings to system-provided condition variables where possible. Note | |
31 | /// that this module places one additional restriction over the system condition | |
32 | /// variables: each condvar can be used with precisely one mutex at runtime. Any | |
33 | /// attempt to use multiple mutexes on the same condition variable will result | |
34 | /// in a runtime panic. If this is not desired, then the unsafe primitives in | |
35 | /// `sys` do not have this restriction but may result in undefined behavior. | |
36 | /// | |
c34b1796 | 37 | /// # Examples |
1a4d82fc JJ |
38 | /// |
39 | /// ``` | |
40 | /// use std::sync::{Arc, Mutex, Condvar}; | |
85aaf69f | 41 | /// use std::thread; |
1a4d82fc JJ |
42 | /// |
43 | /// let pair = Arc::new((Mutex::new(false), Condvar::new())); | |
44 | /// let pair2 = pair.clone(); | |
45 | /// | |
46 | /// // Inside of our lock, spawn a new thread, and then wait for it to start | |
85aaf69f | 47 | /// thread::spawn(move|| { |
1a4d82fc JJ |
48 | /// let &(ref lock, ref cvar) = &*pair2; |
49 | /// let mut started = lock.lock().unwrap(); | |
50 | /// *started = true; | |
51 | /// cvar.notify_one(); | |
52 | /// }); | |
53 | /// | |
54 | /// // wait for the thread to start up | |
55 | /// let &(ref lock, ref cvar) = &*pair; | |
56 | /// let mut started = lock.lock().unwrap(); | |
57 | /// while !*started { | |
58 | /// started = cvar.wait(started).unwrap(); | |
59 | /// } | |
60 | /// ``` | |
85aaf69f | 61 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
62 | pub struct Condvar { inner: Box<StaticCondvar> } |
63 | ||
1a4d82fc JJ |
64 | /// Statically allocated condition variables. |
65 | /// | |
66 | /// This structure is identical to `Condvar` except that it is suitable for use | |
67 | /// in static initializers for other structures. | |
68 | /// | |
c34b1796 | 69 | /// # Examples |
1a4d82fc JJ |
70 | /// |
71 | /// ``` | |
c1a9b12d SL |
72 | /// #![feature(static_condvar)] |
73 | /// | |
1a4d82fc JJ |
74 | /// use std::sync::{StaticCondvar, CONDVAR_INIT}; |
75 | /// | |
76 | /// static CVAR: StaticCondvar = CONDVAR_INIT; | |
77 | /// ``` | |
d9579d0f | 78 | #[unstable(feature = "static_condvar", |
85aaf69f | 79 | reason = "may be merged with Condvar in the future")] |
1a4d82fc JJ |
80 | pub struct StaticCondvar { |
81 | inner: sys::Condvar, | |
85aaf69f | 82 | mutex: AtomicUsize, |
1a4d82fc JJ |
83 | } |
84 | ||
1a4d82fc | 85 | /// Constant initializer for a statically allocated condition variable. |
d9579d0f | 86 | #[unstable(feature = "static_condvar", |
85aaf69f | 87 | reason = "may be merged with Condvar in the future")] |
62682a34 | 88 | pub const CONDVAR_INIT: StaticCondvar = StaticCondvar::new(); |
1a4d82fc JJ |
89 | |
90 | impl Condvar { | |
91 | /// Creates a new condition variable which is ready to be waited on and | |
92 | /// notified. | |
85aaf69f | 93 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
94 | pub fn new() -> Condvar { |
95 | Condvar { | |
96 | inner: box StaticCondvar { | |
62682a34 | 97 | inner: sys::Condvar::new(), |
85aaf69f | 98 | mutex: AtomicUsize::new(0), |
1a4d82fc JJ |
99 | } |
100 | } | |
101 | } | |
102 | ||
9346a6ac | 103 | /// Blocks the current thread until this condition variable receives a |
1a4d82fc JJ |
104 | /// notification. |
105 | /// | |
106 | /// This function will atomically unlock the mutex specified (represented by | |
107 | /// `mutex_guard`) and block the current thread. This means that any calls | |
108 | /// to `notify_*()` which happen logically after the mutex is unlocked are | |
109 | /// candidates to wake this thread up. When this function call returns, the | |
110 | /// lock specified will have been re-acquired. | |
111 | /// | |
112 | /// Note that this function is susceptible to spurious wakeups. Condition | |
113 | /// variables normally have a boolean predicate associated with them, and | |
114 | /// the predicate must always be checked each time this function returns to | |
115 | /// protect against spurious wakeups. | |
116 | /// | |
117 | /// # Failure | |
118 | /// | |
119 | /// This function will return an error if the mutex being waited on is | |
120 | /// poisoned when this thread re-acquires the lock. For more information, | |
121 | /// see information about poisoning on the Mutex type. | |
122 | /// | |
123 | /// # Panics | |
124 | /// | |
125 | /// This function will `panic!()` if it is used with more than one mutex | |
126 | /// over time. Each condition variable is dynamically bound to exactly one | |
127 | /// mutex to ensure defined behavior across platforms. If this functionality | |
128 | /// is not desired, then unsafe primitives in `sys` are provided. | |
85aaf69f | 129 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
130 | pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) |
131 | -> LockResult<MutexGuard<'a, T>> { | |
132 | unsafe { | |
133 | let me: &'static Condvar = &*(self as *const _); | |
134 | me.inner.wait(guard) | |
135 | } | |
136 | } | |
137 | ||
9346a6ac | 138 | /// Waits on this condition variable for a notification, timing out after a |
1a4d82fc JJ |
139 | /// specified duration. |
140 | /// | |
c34b1796 AL |
141 | /// The semantics of this function are equivalent to `wait()` |
142 | /// except that the thread will be blocked for roughly no longer | |
143 | /// than `ms` milliseconds. This method should not be used for | |
144 | /// precise timing due to anomalies such as preemption or platform | |
145 | /// differences that may not cause the maximum amount of time | |
146 | /// waited to be precisely `ms`. | |
1a4d82fc | 147 | /// |
c34b1796 AL |
148 | /// The returned boolean is `false` only if the timeout is known |
149 | /// to have elapsed. | |
1a4d82fc JJ |
150 | /// |
151 | /// Like `wait`, the lock specified will be re-acquired when this function | |
152 | /// returns, regardless of whether the timeout elapsed or not. | |
c34b1796 AL |
153 | #[stable(feature = "rust1", since = "1.0.0")] |
154 | pub fn wait_timeout_ms<'a, T>(&self, guard: MutexGuard<'a, T>, ms: u32) | |
155 | -> LockResult<(MutexGuard<'a, T>, bool)> { | |
1a4d82fc JJ |
156 | unsafe { |
157 | let me: &'static Condvar = &*(self as *const _); | |
c34b1796 | 158 | me.inner.wait_timeout_ms(guard, ms) |
1a4d82fc JJ |
159 | } |
160 | } | |
161 | ||
d9579d0f AL |
162 | /// Waits on this condition variable for a notification, timing out after a |
163 | /// specified duration. | |
164 | /// | |
165 | /// The semantics of this function are equivalent to `wait()` except that | |
166 | /// the thread will be blocked for roughly no longer than `dur`. This | |
167 | /// method should not be used for precise timing due to anomalies such as | |
168 | /// preemption or platform differences that may not cause the maximum | |
169 | /// amount of time waited to be precisely `dur`. | |
170 | /// | |
171 | /// The returned boolean is `false` only if the timeout is known | |
172 | /// to have elapsed. | |
173 | /// | |
174 | /// Like `wait`, the lock specified will be re-acquired when this function | |
175 | /// returns, regardless of whether the timeout elapsed or not. | |
176 | #[unstable(feature = "wait_timeout", reason = "waiting for Duration")] | |
177 | pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, | |
178 | dur: Duration) | |
179 | -> LockResult<(MutexGuard<'a, T>, bool)> { | |
180 | unsafe { | |
181 | let me: &'static Condvar = &*(self as *const _); | |
182 | me.inner.wait_timeout(guard, dur) | |
183 | } | |
184 | } | |
185 | ||
9346a6ac | 186 | /// Waits on this condition variable for a notification, timing out after a |
85aaf69f SL |
187 | /// specified duration. |
188 | /// | |
189 | /// The semantics of this function are equivalent to `wait_timeout` except | |
190 | /// that the implementation will repeatedly wait while the duration has not | |
191 | /// passed and the provided function returns `false`. | |
c34b1796 AL |
192 | #[unstable(feature = "wait_timeout_with", |
193 | reason = "unsure if this API is broadly needed or what form it should take")] | |
85aaf69f SL |
194 | pub fn wait_timeout_with<'a, T, F>(&self, |
195 | guard: MutexGuard<'a, T>, | |
196 | dur: Duration, | |
197 | f: F) | |
198 | -> LockResult<(MutexGuard<'a, T>, bool)> | |
199 | where F: FnMut(LockResult<&mut T>) -> bool { | |
200 | unsafe { | |
201 | let me: &'static Condvar = &*(self as *const _); | |
202 | me.inner.wait_timeout_with(guard, dur, f) | |
203 | } | |
204 | } | |
205 | ||
9346a6ac | 206 | /// Wakes up one blocked thread on this condvar. |
1a4d82fc JJ |
207 | /// |
208 | /// If there is a blocked thread on this condition variable, then it will | |
209 | /// be woken up from its call to `wait` or `wait_timeout`. Calls to | |
210 | /// `notify_one` are not buffered in any way. | |
211 | /// | |
85aaf69f SL |
212 | /// To wake up all threads, see `notify_all()`. |
213 | #[stable(feature = "rust1", since = "1.0.0")] | |
1a4d82fc JJ |
214 | pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } |
215 | ||
9346a6ac | 216 | /// Wakes up all blocked threads on this condvar. |
1a4d82fc JJ |
217 | /// |
218 | /// This method will ensure that any current waiters on the condition | |
219 | /// variable are awoken. Calls to `notify_all()` are not buffered in any | |
220 | /// way. | |
221 | /// | |
222 | /// To wake up only one thread, see `notify_one()`. | |
85aaf69f | 223 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
224 | pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } |
225 | } | |
226 | ||
85aaf69f | 227 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
228 | impl Drop for Condvar { |
229 | fn drop(&mut self) { | |
230 | unsafe { self.inner.inner.destroy() } | |
231 | } | |
232 | } | |
233 | ||
234 | impl StaticCondvar { | |
62682a34 SL |
235 | /// Creates a new condition variable |
236 | #[unstable(feature = "static_condvar", | |
237 | reason = "may be merged with Condvar in the future")] | |
238 | pub const fn new() -> StaticCondvar { | |
239 | StaticCondvar { | |
240 | inner: sys::Condvar::new(), | |
241 | mutex: AtomicUsize::new(0), | |
242 | } | |
243 | } | |
244 | ||
9346a6ac | 245 | /// Blocks the current thread until this condition variable receives a |
1a4d82fc JJ |
246 | /// notification. |
247 | /// | |
248 | /// See `Condvar::wait`. | |
d9579d0f | 249 | #[unstable(feature = "static_condvar", |
85aaf69f | 250 | reason = "may be merged with Condvar in the future")] |
1a4d82fc JJ |
251 | pub fn wait<'a, T>(&'static self, guard: MutexGuard<'a, T>) |
252 | -> LockResult<MutexGuard<'a, T>> { | |
253 | let poisoned = unsafe { | |
254 | let lock = mutex::guard_lock(&guard); | |
255 | self.verify(lock); | |
256 | self.inner.wait(lock); | |
257 | mutex::guard_poison(&guard).get() | |
258 | }; | |
259 | if poisoned { | |
85aaf69f | 260 | Err(PoisonError::new(guard)) |
1a4d82fc JJ |
261 | } else { |
262 | Ok(guard) | |
263 | } | |
264 | } | |
265 | ||
9346a6ac | 266 | /// Waits on this condition variable for a notification, timing out after a |
1a4d82fc JJ |
267 | /// specified duration. |
268 | /// | |
269 | /// See `Condvar::wait_timeout`. | |
d9579d0f | 270 | #[unstable(feature = "static_condvar", |
85aaf69f | 271 | reason = "may be merged with Condvar in the future")] |
c34b1796 AL |
272 | pub fn wait_timeout_ms<'a, T>(&'static self, guard: MutexGuard<'a, T>, ms: u32) |
273 | -> LockResult<(MutexGuard<'a, T>, bool)> { | |
d9579d0f AL |
274 | self.wait_timeout(guard, Duration::from_millis(ms as u64)) |
275 | } | |
276 | ||
277 | /// Waits on this condition variable for a notification, timing out after a | |
278 | /// specified duration. | |
279 | /// | |
280 | /// See `Condvar::wait_timeout`. | |
281 | #[unstable(feature = "static_condvar", | |
282 | reason = "may be merged with Condvar in the future")] | |
283 | pub fn wait_timeout<'a, T>(&'static self, | |
284 | guard: MutexGuard<'a, T>, | |
285 | timeout: Duration) | |
286 | -> LockResult<(MutexGuard<'a, T>, bool)> { | |
1a4d82fc JJ |
287 | let (poisoned, success) = unsafe { |
288 | let lock = mutex::guard_lock(&guard); | |
289 | self.verify(lock); | |
d9579d0f | 290 | let success = self.inner.wait_timeout(lock, timeout); |
1a4d82fc JJ |
291 | (mutex::guard_poison(&guard).get(), success) |
292 | }; | |
293 | if poisoned { | |
85aaf69f | 294 | Err(PoisonError::new((guard, success))) |
1a4d82fc JJ |
295 | } else { |
296 | Ok((guard, success)) | |
297 | } | |
298 | } | |
299 | ||
9346a6ac | 300 | /// Waits on this condition variable for a notification, timing out after a |
85aaf69f SL |
301 | /// specified duration. |
302 | /// | |
303 | /// The implementation will repeatedly wait while the duration has not | |
304 | /// passed and the function returns `false`. | |
305 | /// | |
306 | /// See `Condvar::wait_timeout_with`. | |
d9579d0f | 307 | #[unstable(feature = "static_condvar", |
85aaf69f SL |
308 | reason = "may be merged with Condvar in the future")] |
309 | pub fn wait_timeout_with<'a, T, F>(&'static self, | |
310 | guard: MutexGuard<'a, T>, | |
311 | dur: Duration, | |
312 | mut f: F) | |
313 | -> LockResult<(MutexGuard<'a, T>, bool)> | |
314 | where F: FnMut(LockResult<&mut T>) -> bool { | |
d9579d0f AL |
315 | // This could be made more efficient by pushing the implementation into |
316 | // sys::condvar | |
85aaf69f SL |
317 | let start = SteadyTime::now(); |
318 | let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard); | |
319 | while !f(guard_result | |
320 | .as_mut() | |
321 | .map(|g| &mut **g) | |
322 | .map_err(|e| PoisonError::new(&mut **e.get_mut()))) { | |
323 | let now = SteadyTime::now(); | |
324 | let consumed = &now - &start; | |
325 | let guard = guard_result.unwrap_or_else(|e| e.into_inner()); | |
d9579d0f AL |
326 | let (new_guard_result, no_timeout) = if consumed > dur { |
327 | (Ok(guard), false) | |
328 | } else { | |
329 | match self.wait_timeout(guard, dur - consumed) { | |
330 | Ok((new_guard, no_timeout)) => (Ok(new_guard), no_timeout), | |
331 | Err(err) => { | |
332 | let (new_guard, no_timeout) = err.into_inner(); | |
333 | (Err(PoisonError::new(new_guard)), no_timeout) | |
334 | } | |
85aaf69f SL |
335 | } |
336 | }; | |
337 | guard_result = new_guard_result; | |
338 | if !no_timeout { | |
339 | let result = f(guard_result | |
340 | .as_mut() | |
341 | .map(|g| &mut **g) | |
342 | .map_err(|e| PoisonError::new(&mut **e.get_mut()))); | |
343 | return poison::map_result(guard_result, |g| (g, result)); | |
344 | } | |
345 | } | |
346 | ||
347 | poison::map_result(guard_result, |g| (g, true)) | |
348 | } | |
349 | ||
9346a6ac | 350 | /// Wakes up one blocked thread on this condvar. |
1a4d82fc JJ |
351 | /// |
352 | /// See `Condvar::notify_one`. | |
d9579d0f | 353 | #[unstable(feature = "static_condvar", |
85aaf69f | 354 | reason = "may be merged with Condvar in the future")] |
1a4d82fc JJ |
355 | pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } |
356 | ||
9346a6ac | 357 | /// Wakes up all blocked threads on this condvar. |
1a4d82fc JJ |
358 | /// |
359 | /// See `Condvar::notify_all`. | |
d9579d0f | 360 | #[unstable(feature = "static_condvar", |
85aaf69f | 361 | reason = "may be merged with Condvar in the future")] |
1a4d82fc JJ |
362 | pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } |
363 | ||
9346a6ac | 364 | /// Deallocates all resources associated with this static condvar. |
1a4d82fc JJ |
365 | /// |
366 | /// This method is unsafe to call as there is no guarantee that there are no | |
367 | /// active users of the condvar, and this also doesn't prevent any future | |
368 | /// users of the condvar. This method is required to be called to not leak | |
369 | /// memory on all platforms. | |
d9579d0f | 370 | #[unstable(feature = "static_condvar", |
85aaf69f | 371 | reason = "may be merged with Condvar in the future")] |
1a4d82fc JJ |
372 | pub unsafe fn destroy(&'static self) { |
373 | self.inner.destroy() | |
374 | } | |
375 | ||
376 | fn verify(&self, mutex: &sys_mutex::Mutex) { | |
c34b1796 | 377 | let addr = mutex as *const _ as usize; |
1a4d82fc JJ |
378 | match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { |
379 | // If we got out 0, then we have successfully bound the mutex to | |
380 | // this cvar. | |
381 | 0 => {} | |
382 | ||
383 | // If we get out a value that's the same as `addr`, then someone | |
384 | // already beat us to the punch. | |
385 | n if n == addr => {} | |
386 | ||
387 | // Anything else and we're using more than one mutex on this cvar, | |
388 | // which is currently disallowed. | |
389 | _ => panic!("attempted to use a condition variable with two \ | |
390 | mutexes"), | |
391 | } | |
392 | } | |
393 | } | |
394 | ||
395 | #[cfg(test)] | |
396 | mod tests { | |
397 | use prelude::v1::*; | |
398 | ||
62682a34 | 399 | use super::StaticCondvar; |
1a4d82fc | 400 | use sync::mpsc::channel; |
62682a34 SL |
401 | use sync::{StaticMutex, Condvar, Mutex, Arc}; |
402 | use sync::atomic::{AtomicUsize, Ordering}; | |
85aaf69f | 403 | use thread; |
1a4d82fc | 404 | use time::Duration; |
c34b1796 | 405 | use u32; |
1a4d82fc JJ |
406 | |
407 | #[test] | |
408 | fn smoke() { | |
409 | let c = Condvar::new(); | |
410 | c.notify_one(); | |
411 | c.notify_all(); | |
412 | } | |
413 | ||
414 | #[test] | |
415 | fn static_smoke() { | |
62682a34 | 416 | static C: StaticCondvar = StaticCondvar::new(); |
1a4d82fc JJ |
417 | C.notify_one(); |
418 | C.notify_all(); | |
419 | unsafe { C.destroy(); } | |
420 | } | |
421 | ||
422 | #[test] | |
423 | fn notify_one() { | |
62682a34 SL |
424 | static C: StaticCondvar = StaticCondvar::new(); |
425 | static M: StaticMutex = StaticMutex::new(); | |
1a4d82fc JJ |
426 | |
427 | let g = M.lock().unwrap(); | |
85aaf69f | 428 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
429 | let _g = M.lock().unwrap(); |
430 | C.notify_one(); | |
431 | }); | |
432 | let g = C.wait(g).unwrap(); | |
433 | drop(g); | |
434 | unsafe { C.destroy(); M.destroy(); } | |
435 | } | |
436 | ||
437 | #[test] | |
438 | fn notify_all() { | |
c34b1796 | 439 | const N: usize = 10; |
1a4d82fc JJ |
440 | |
441 | let data = Arc::new((Mutex::new(0), Condvar::new())); | |
442 | let (tx, rx) = channel(); | |
85aaf69f | 443 | for _ in 0..N { |
1a4d82fc JJ |
444 | let data = data.clone(); |
445 | let tx = tx.clone(); | |
85aaf69f | 446 | thread::spawn(move|| { |
1a4d82fc JJ |
447 | let &(ref lock, ref cond) = &*data; |
448 | let mut cnt = lock.lock().unwrap(); | |
449 | *cnt += 1; | |
450 | if *cnt == N { | |
451 | tx.send(()).unwrap(); | |
452 | } | |
453 | while *cnt != 0 { | |
454 | cnt = cond.wait(cnt).unwrap(); | |
455 | } | |
456 | tx.send(()).unwrap(); | |
457 | }); | |
458 | } | |
459 | drop(tx); | |
460 | ||
461 | let &(ref lock, ref cond) = &*data; | |
462 | rx.recv().unwrap(); | |
463 | let mut cnt = lock.lock().unwrap(); | |
464 | *cnt = 0; | |
465 | cond.notify_all(); | |
466 | drop(cnt); | |
467 | ||
85aaf69f | 468 | for _ in 0..N { |
1a4d82fc JJ |
469 | rx.recv().unwrap(); |
470 | } | |
471 | } | |
472 | ||
473 | #[test] | |
c34b1796 | 474 | fn wait_timeout_ms() { |
62682a34 SL |
475 | static C: StaticCondvar = StaticCondvar::new(); |
476 | static M: StaticMutex = StaticMutex::new(); | |
1a4d82fc JJ |
477 | |
478 | let g = M.lock().unwrap(); | |
c34b1796 | 479 | let (g, _no_timeout) = C.wait_timeout_ms(g, 1).unwrap(); |
85aaf69f SL |
480 | // spurious wakeups mean this isn't necessarily true |
481 | // assert!(!no_timeout); | |
482 | let _t = thread::spawn(move || { | |
1a4d82fc JJ |
483 | let _g = M.lock().unwrap(); |
484 | C.notify_one(); | |
485 | }); | |
c34b1796 | 486 | let (g, no_timeout) = C.wait_timeout_ms(g, u32::MAX).unwrap(); |
85aaf69f | 487 | assert!(no_timeout); |
1a4d82fc JJ |
488 | drop(g); |
489 | unsafe { C.destroy(); M.destroy(); } | |
490 | } | |
491 | ||
85aaf69f SL |
492 | #[test] |
493 | fn wait_timeout_with() { | |
62682a34 SL |
494 | static C: StaticCondvar = StaticCondvar::new(); |
495 | static M: StaticMutex = StaticMutex::new(); | |
496 | static S: AtomicUsize = AtomicUsize::new(0); | |
85aaf69f SL |
497 | |
498 | let g = M.lock().unwrap(); | |
d9579d0f AL |
499 | let (g, success) = C.wait_timeout_with(g, Duration::new(0, 1000), |_| { |
500 | false | |
501 | }).unwrap(); | |
85aaf69f SL |
502 | assert!(!success); |
503 | ||
504 | let (tx, rx) = channel(); | |
505 | let _t = thread::spawn(move || { | |
506 | rx.recv().unwrap(); | |
507 | let g = M.lock().unwrap(); | |
508 | S.store(1, Ordering::SeqCst); | |
509 | C.notify_one(); | |
510 | drop(g); | |
511 | ||
512 | rx.recv().unwrap(); | |
513 | let g = M.lock().unwrap(); | |
514 | S.store(2, Ordering::SeqCst); | |
515 | C.notify_one(); | |
516 | drop(g); | |
517 | ||
518 | rx.recv().unwrap(); | |
519 | let _g = M.lock().unwrap(); | |
520 | S.store(3, Ordering::SeqCst); | |
521 | C.notify_one(); | |
522 | }); | |
523 | ||
524 | let mut state = 0; | |
d9579d0f AL |
525 | let day = 24 * 60 * 60; |
526 | let (_g, success) = C.wait_timeout_with(g, Duration::new(day, 0), |_| { | |
85aaf69f SL |
527 | assert_eq!(state, S.load(Ordering::SeqCst)); |
528 | tx.send(()).unwrap(); | |
529 | state += 1; | |
530 | match state { | |
531 | 1|2 => false, | |
532 | _ => true, | |
533 | } | |
534 | }).unwrap(); | |
535 | assert!(success); | |
536 | } | |
537 | ||
1a4d82fc | 538 | #[test] |
c34b1796 | 539 | #[should_panic] |
1a4d82fc | 540 | fn two_mutexes() { |
62682a34 SL |
541 | static M1: StaticMutex = StaticMutex::new(); |
542 | static M2: StaticMutex = StaticMutex::new(); | |
543 | static C: StaticCondvar = StaticCondvar::new(); | |
1a4d82fc JJ |
544 | |
545 | let mut g = M1.lock().unwrap(); | |
85aaf69f | 546 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
547 | let _g = M1.lock().unwrap(); |
548 | C.notify_one(); | |
549 | }); | |
550 | g = C.wait(g).unwrap(); | |
551 | drop(g); | |
552 | ||
553 | let _ = C.wait(M2.lock().unwrap()).unwrap(); | |
554 | } | |
555 | } |