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