]>
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 | ||
13 | use cell::UnsafeCell; | |
9346a6ac | 14 | use fmt; |
1a4d82fc | 15 | use marker; |
b039eaaf | 16 | use mem; |
1a4d82fc | 17 | use ops::{Deref, DerefMut}; |
b039eaaf | 18 | use ptr; |
1a4d82fc | 19 | use sys_common::mutex as sys; |
9346a6ac | 20 | use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; |
1a4d82fc JJ |
21 | |
22 | /// A mutual exclusion primitive useful for protecting shared data | |
23 | /// | |
24 | /// This mutex will block threads waiting for the lock to become available. The | |
25 | /// mutex can also be statically initialized or created via a `new` | |
26 | /// constructor. Each mutex has a type parameter which represents the data that | |
27 | /// it is protecting. The data can only be accessed through the RAII guards | |
28 | /// returned from `lock` and `try_lock`, which guarantees that the data is only | |
29 | /// ever accessed when the mutex is locked. | |
30 | /// | |
31 | /// # Poisoning | |
32 | /// | |
33 | /// The mutexes in this module implement a strategy called "poisoning" where a | |
34 | /// mutex is considered poisoned whenever a thread panics while holding the | |
bd371182 | 35 | /// lock. Once a mutex is poisoned, all other threads are unable to access the |
1a4d82fc JJ |
36 | /// data by default as it is likely tainted (some invariant is not being |
37 | /// upheld). | |
38 | /// | |
39 | /// For a mutex, this means that the `lock` and `try_lock` methods return a | |
40 | /// `Result` which indicates whether a mutex has been poisoned or not. Most | |
41 | /// usage of a mutex will simply `unwrap()` these results, propagating panics | |
42 | /// among threads to ensure that a possibly invalid invariant is not witnessed. | |
43 | /// | |
44 | /// A poisoned mutex, however, does not prevent all access to the underlying | |
c34b1796 | 45 | /// data. The `PoisonError` type has an `into_inner` method which will return |
1a4d82fc JJ |
46 | /// the guard that would have otherwise been returned on a successful lock. This |
47 | /// allows access to the data, despite the lock being poisoned. | |
48 | /// | |
49 | /// # Examples | |
50 | /// | |
c34b1796 | 51 | /// ``` |
1a4d82fc | 52 | /// use std::sync::{Arc, Mutex}; |
85aaf69f | 53 | /// use std::thread; |
1a4d82fc JJ |
54 | /// use std::sync::mpsc::channel; |
55 | /// | |
c34b1796 | 56 | /// const N: usize = 10; |
1a4d82fc JJ |
57 | /// |
58 | /// // Spawn a few threads to increment a shared variable (non-atomically), and | |
59 | /// // let the main thread know once all increments are done. | |
60 | /// // | |
bd371182 | 61 | /// // Here we're using an Arc to share memory among threads, and the data inside |
1a4d82fc JJ |
62 | /// // the Arc is protected with a mutex. |
63 | /// let data = Arc::new(Mutex::new(0)); | |
64 | /// | |
65 | /// let (tx, rx) = channel(); | |
85aaf69f | 66 | /// for _ in 0..10 { |
1a4d82fc | 67 | /// let (data, tx) = (data.clone(), tx.clone()); |
85aaf69f | 68 | /// thread::spawn(move || { |
92a42be0 | 69 | /// // The shared state can only be accessed once the lock is held. |
1a4d82fc JJ |
70 | /// // Our non-atomic increment is safe because we're the only thread |
71 | /// // which can access the shared state when the lock is held. | |
72 | /// // | |
73 | /// // We unwrap() the return value to assert that we are not expecting | |
bd371182 | 74 | /// // threads to ever fail while holding the lock. |
1a4d82fc JJ |
75 | /// let mut data = data.lock().unwrap(); |
76 | /// *data += 1; | |
77 | /// if *data == N { | |
78 | /// tx.send(()).unwrap(); | |
79 | /// } | |
80 | /// // the lock is unlocked here when `data` goes out of scope. | |
81 | /// }); | |
82 | /// } | |
83 | /// | |
84 | /// rx.recv().unwrap(); | |
85 | /// ``` | |
86 | /// | |
87 | /// To recover from a poisoned mutex: | |
88 | /// | |
c34b1796 | 89 | /// ``` |
1a4d82fc | 90 | /// use std::sync::{Arc, Mutex}; |
85aaf69f | 91 | /// use std::thread; |
1a4d82fc | 92 | /// |
85aaf69f | 93 | /// let lock = Arc::new(Mutex::new(0_u32)); |
1a4d82fc JJ |
94 | /// let lock2 = lock.clone(); |
95 | /// | |
85aaf69f | 96 | /// let _ = thread::spawn(move || -> () { |
1a4d82fc JJ |
97 | /// // This thread will acquire the mutex first, unwrapping the result of |
98 | /// // `lock` because the lock has not been poisoned. | |
99 | /// let _lock = lock2.lock().unwrap(); | |
100 | /// | |
101 | /// // This panic while holding the lock (`_guard` is in scope) will poison | |
102 | /// // the mutex. | |
103 | /// panic!(); | |
104 | /// }).join(); | |
105 | /// | |
106 | /// // The lock is poisoned by this point, but the returned result can be | |
107 | /// // pattern matched on to return the underlying guard on both branches. | |
108 | /// let mut guard = match lock.lock() { | |
109 | /// Ok(guard) => guard, | |
c34b1796 | 110 | /// Err(poisoned) => poisoned.into_inner(), |
1a4d82fc JJ |
111 | /// }; |
112 | /// | |
113 | /// *guard += 1; | |
114 | /// ``` | |
85aaf69f | 115 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 116 | pub struct Mutex<T: ?Sized> { |
1a4d82fc JJ |
117 | // Note that this static mutex is in a *box*, not inlined into the struct |
118 | // itself. Once a native mutex has been used once, its address can never | |
119 | // change (it can't be moved). This mutex type can be safely moved at any | |
120 | // time, so to ensure that the native mutex is used correctly we box the | |
121 | // inner lock to give it a constant address. | |
122 | inner: Box<StaticMutex>, | |
123 | data: UnsafeCell<T>, | |
124 | } | |
125 | ||
c34b1796 AL |
126 | // these are the only places where `T: Send` matters; all other |
127 | // functionality works fine on a single thread. | |
92a42be0 | 128 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 129 | unsafe impl<T: ?Sized + Send> Send for Mutex<T> { } |
92a42be0 | 130 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 131 | unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { } |
1a4d82fc JJ |
132 | |
133 | /// The static mutex type is provided to allow for static allocation of mutexes. | |
134 | /// | |
135 | /// Note that this is a separate type because using a Mutex correctly means that | |
136 | /// it needs to have a destructor run. In Rust, statics are not allowed to have | |
137 | /// destructors. As a result, a `StaticMutex` has one extra method when compared | |
138 | /// to a `Mutex`, a `destroy` method. This method is unsafe to call, and | |
139 | /// documentation can be found directly on the method. | |
140 | /// | |
c34b1796 | 141 | /// # Examples |
1a4d82fc | 142 | /// |
c34b1796 | 143 | /// ``` |
c1a9b12d SL |
144 | /// #![feature(static_mutex)] |
145 | /// | |
1a4d82fc JJ |
146 | /// use std::sync::{StaticMutex, MUTEX_INIT}; |
147 | /// | |
148 | /// static LOCK: StaticMutex = MUTEX_INIT; | |
149 | /// | |
150 | /// { | |
151 | /// let _g = LOCK.lock().unwrap(); | |
152 | /// // do some productive work | |
153 | /// } | |
154 | /// // lock is unlocked here. | |
155 | /// ``` | |
62682a34 | 156 | #[unstable(feature = "static_mutex", |
e9174d1e SL |
157 | reason = "may be merged with Mutex in the future", |
158 | issue = "27717")] | |
1a4d82fc JJ |
159 | pub struct StaticMutex { |
160 | lock: sys::Mutex, | |
161 | poison: poison::Flag, | |
162 | } | |
163 | ||
1a4d82fc JJ |
164 | /// An RAII implementation of a "scoped lock" of a mutex. When this structure is |
165 | /// dropped (falls out of scope), the lock will be unlocked. | |
166 | /// | |
167 | /// The data protected by the mutex can be access through this guard via its | |
d9579d0f | 168 | /// `Deref` and `DerefMut` implementations |
1a4d82fc | 169 | #[must_use] |
85aaf69f | 170 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 171 | pub struct MutexGuard<'a, T: ?Sized + 'a> { |
1a4d82fc JJ |
172 | // funny underscores due to how Deref/DerefMut currently work (they |
173 | // disregard field privacy). | |
174 | __lock: &'a StaticMutex, | |
175 | __data: &'a UnsafeCell<T>, | |
176 | __poison: poison::Guard, | |
1a4d82fc JJ |
177 | } |
178 | ||
92a42be0 | 179 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 180 | impl<'a, T: ?Sized> !marker::Send for MutexGuard<'a, T> {} |
85aaf69f | 181 | |
1a4d82fc JJ |
182 | /// Static initialization of a mutex. This constant can be used to initialize |
183 | /// other mutex constants. | |
62682a34 | 184 | #[unstable(feature = "static_mutex", |
e9174d1e SL |
185 | reason = "may be merged with Mutex in the future", |
186 | issue = "27717")] | |
62682a34 | 187 | pub const MUTEX_INIT: StaticMutex = StaticMutex::new(); |
1a4d82fc | 188 | |
c34b1796 | 189 | impl<T> Mutex<T> { |
1a4d82fc | 190 | /// Creates a new mutex in an unlocked state ready for use. |
85aaf69f | 191 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
192 | pub fn new(t: T) -> Mutex<T> { |
193 | Mutex { | |
62682a34 | 194 | inner: box StaticMutex::new(), |
1a4d82fc JJ |
195 | data: UnsafeCell::new(t), |
196 | } | |
197 | } | |
d9579d0f | 198 | } |
1a4d82fc | 199 | |
d9579d0f | 200 | impl<T: ?Sized> Mutex<T> { |
bd371182 | 201 | /// Acquires a mutex, blocking the current thread until it is able to do so. |
1a4d82fc | 202 | /// |
bd371182 AL |
203 | /// This function will block the local thread until it is available to acquire |
204 | /// the mutex. Upon returning, the thread is the only thread with the mutex | |
1a4d82fc JJ |
205 | /// held. An RAII guard is returned to allow scoped unlock of the lock. When |
206 | /// the guard goes out of scope, the mutex will be unlocked. | |
207 | /// | |
208 | /// # Failure | |
209 | /// | |
210 | /// If another user of this mutex panicked while holding the mutex, then | |
211 | /// this call will return an error once the mutex is acquired. | |
85aaf69f | 212 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
213 | pub fn lock(&self) -> LockResult<MutexGuard<T>> { |
214 | unsafe { self.inner.lock.lock() } | |
215 | MutexGuard::new(&*self.inner, &self.data) | |
216 | } | |
217 | ||
218 | /// Attempts to acquire this lock. | |
219 | /// | |
9346a6ac | 220 | /// If the lock could not be acquired at this time, then `Err` is returned. |
1a4d82fc JJ |
221 | /// Otherwise, an RAII guard is returned. The lock will be unlocked when the |
222 | /// guard is dropped. | |
223 | /// | |
224 | /// This function does not block. | |
225 | /// | |
226 | /// # Failure | |
227 | /// | |
228 | /// If another user of this mutex panicked while holding the mutex, then | |
229 | /// this call will return failure if the mutex would otherwise be | |
230 | /// acquired. | |
85aaf69f | 231 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
232 | pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> { |
233 | if unsafe { self.inner.lock.try_lock() } { | |
234 | Ok(try!(MutexGuard::new(&*self.inner, &self.data))) | |
235 | } else { | |
236 | Err(TryLockError::WouldBlock) | |
237 | } | |
238 | } | |
85aaf69f | 239 | |
9346a6ac | 240 | /// Determines whether the lock is poisoned. |
85aaf69f SL |
241 | /// |
242 | /// If another thread is active, the lock can still become poisoned at any | |
243 | /// time. You should not trust a `false` value for program correctness | |
244 | /// without additional synchronization. | |
245 | #[inline] | |
62682a34 | 246 | #[stable(feature = "sync_poison", since = "1.2.0")] |
85aaf69f SL |
247 | pub fn is_poisoned(&self) -> bool { |
248 | self.inner.poison.get() | |
249 | } | |
b039eaaf SL |
250 | |
251 | /// Consumes this mutex, returning the underlying data. | |
252 | /// | |
253 | /// # Failure | |
254 | /// | |
255 | /// If another user of this mutex panicked while holding the mutex, then | |
256 | /// this call will return an error instead. | |
92a42be0 | 257 | #[stable(feature = "mutex_into_inner", since = "1.6.0")] |
b039eaaf SL |
258 | pub fn into_inner(self) -> LockResult<T> where T: Sized { |
259 | // We know statically that there are no outstanding references to | |
260 | // `self` so there's no need to lock the inner StaticMutex. | |
261 | // | |
262 | // To get the inner value, we'd like to call `data.into_inner()`, | |
263 | // but because `Mutex` impl-s `Drop`, we can't move out of it, so | |
264 | // we'll have to destructure it manually instead. | |
265 | unsafe { | |
266 | // Like `let Mutex { inner, data } = self`. | |
267 | let (inner, data) = { | |
268 | let Mutex { ref inner, ref data } = self; | |
269 | (ptr::read(inner), ptr::read(data)) | |
270 | }; | |
271 | mem::forget(self); | |
272 | inner.lock.destroy(); // Keep in sync with the `Drop` impl. | |
273 | ||
274 | poison::map_result(inner.poison.borrow(), |_| data.into_inner()) | |
275 | } | |
276 | } | |
277 | ||
278 | /// Returns a mutable reference to the underlying data. | |
279 | /// | |
280 | /// Since this call borrows the `Mutex` mutably, no actual locking needs to | |
281 | /// take place---the mutable borrow statically guarantees no locks exist. | |
282 | /// | |
283 | /// # Failure | |
284 | /// | |
285 | /// If another user of this mutex panicked while holding the mutex, then | |
286 | /// this call will return an error instead. | |
92a42be0 | 287 | #[stable(feature = "mutex_get_mut", since = "1.6.0")] |
b039eaaf SL |
288 | pub fn get_mut(&mut self) -> LockResult<&mut T> { |
289 | // We know statically that there are no other references to `self`, so | |
290 | // there's no need to lock the inner StaticMutex. | |
291 | let data = unsafe { &mut *self.data.get() }; | |
292 | poison::map_result(self.inner.poison.borrow(), |_| data ) | |
293 | } | |
1a4d82fc JJ |
294 | } |
295 | ||
85aaf69f | 296 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 297 | impl<T: ?Sized> Drop for Mutex<T> { |
b039eaaf | 298 | #[unsafe_destructor_blind_to_params] |
1a4d82fc JJ |
299 | fn drop(&mut self) { |
300 | // This is actually safe b/c we know that there is no further usage of | |
301 | // this mutex (it's up to the user to arrange for a mutex to get | |
302 | // dropped, that's not our job) | |
b039eaaf SL |
303 | // |
304 | // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`. | |
1a4d82fc JJ |
305 | unsafe { self.inner.lock.destroy() } |
306 | } | |
307 | } | |
308 | ||
c34b1796 | 309 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 310 | impl<T: ?Sized + fmt::Debug + 'static> fmt::Debug for Mutex<T> { |
c34b1796 AL |
311 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
312 | match self.try_lock() { | |
d9579d0f | 313 | Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard), |
c34b1796 | 314 | Err(TryLockError::Poisoned(err)) => { |
d9579d0f | 315 | write!(f, "Mutex {{ data: Poisoned({:?}) }}", &**err.get_ref()) |
c34b1796 AL |
316 | }, |
317 | Err(TryLockError::WouldBlock) => write!(f, "Mutex {{ <locked> }}") | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
1a4d82fc JJ |
322 | struct Dummy(UnsafeCell<()>); |
323 | unsafe impl Sync for Dummy {} | |
62682a34 | 324 | static DUMMY: Dummy = Dummy(UnsafeCell::new(())); |
1a4d82fc | 325 | |
62682a34 | 326 | #[unstable(feature = "static_mutex", |
e9174d1e SL |
327 | reason = "may be merged with Mutex in the future", |
328 | issue = "27717")] | |
1a4d82fc | 329 | impl StaticMutex { |
62682a34 SL |
330 | /// Creates a new mutex in an unlocked state ready for use. |
331 | pub const fn new() -> StaticMutex { | |
332 | StaticMutex { | |
333 | lock: sys::Mutex::new(), | |
334 | poison: poison::Flag::new(), | |
335 | } | |
336 | } | |
337 | ||
1a4d82fc JJ |
338 | /// Acquires this lock, see `Mutex::lock` |
339 | #[inline] | |
1a4d82fc JJ |
340 | pub fn lock(&'static self) -> LockResult<MutexGuard<()>> { |
341 | unsafe { self.lock.lock() } | |
342 | MutexGuard::new(self, &DUMMY.0) | |
343 | } | |
344 | ||
345 | /// Attempts to grab this lock, see `Mutex::try_lock` | |
346 | #[inline] | |
1a4d82fc JJ |
347 | pub fn try_lock(&'static self) -> TryLockResult<MutexGuard<()>> { |
348 | if unsafe { self.lock.try_lock() } { | |
349 | Ok(try!(MutexGuard::new(self, &DUMMY.0))) | |
350 | } else { | |
351 | Err(TryLockError::WouldBlock) | |
352 | } | |
353 | } | |
354 | ||
355 | /// Deallocates resources associated with this static mutex. | |
356 | /// | |
357 | /// This method is unsafe because it provides no guarantees that there are | |
358 | /// no active users of this mutex, and safety is not guaranteed if there are | |
359 | /// active users of this mutex. | |
360 | /// | |
361 | /// This method is required to ensure that there are no memory leaks on | |
362 | /// *all* platforms. It may be the case that some platforms do not leak | |
363 | /// memory if this method is not called, but this is not guaranteed to be | |
364 | /// true on all platforms. | |
1a4d82fc JJ |
365 | pub unsafe fn destroy(&'static self) { |
366 | self.lock.destroy() | |
367 | } | |
368 | } | |
369 | ||
d9579d0f | 370 | impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { |
85aaf69f | 371 | |
1a4d82fc JJ |
372 | fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>) |
373 | -> LockResult<MutexGuard<'mutex, T>> { | |
374 | poison::map_result(lock.poison.borrow(), |guard| { | |
375 | MutexGuard { | |
376 | __lock: lock, | |
377 | __data: data, | |
378 | __poison: guard, | |
1a4d82fc JJ |
379 | } |
380 | }) | |
381 | } | |
382 | } | |
383 | ||
85aaf69f | 384 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 385 | impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> { |
1a4d82fc JJ |
386 | type Target = T; |
387 | ||
e9174d1e | 388 | fn deref(&self) -> &T { |
1a4d82fc JJ |
389 | unsafe { &*self.__data.get() } |
390 | } | |
391 | } | |
e9174d1e | 392 | |
85aaf69f | 393 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 394 | impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> { |
e9174d1e | 395 | fn deref_mut(&mut self) -> &mut T { |
1a4d82fc JJ |
396 | unsafe { &mut *self.__data.get() } |
397 | } | |
398 | } | |
399 | ||
85aaf69f | 400 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 401 | impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> { |
1a4d82fc JJ |
402 | #[inline] |
403 | fn drop(&mut self) { | |
404 | unsafe { | |
405 | self.__lock.poison.done(&self.__poison); | |
406 | self.__lock.lock.unlock(); | |
407 | } | |
408 | } | |
409 | } | |
410 | ||
d9579d0f | 411 | pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { |
1a4d82fc JJ |
412 | &guard.__lock.lock |
413 | } | |
414 | ||
d9579d0f | 415 | pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { |
1a4d82fc JJ |
416 | &guard.__lock.poison |
417 | } | |
418 | ||
419 | #[cfg(test)] | |
d9579d0f | 420 | mod tests { |
1a4d82fc JJ |
421 | use prelude::v1::*; |
422 | ||
423 | use sync::mpsc::channel; | |
62682a34 | 424 | use sync::{Arc, Mutex, StaticMutex, Condvar}; |
b039eaaf | 425 | use sync::atomic::{AtomicUsize, Ordering}; |
85aaf69f | 426 | use thread; |
1a4d82fc | 427 | |
e9174d1e | 428 | struct Packet<T>(Arc<(Mutex<T>, Condvar)>); |
1a4d82fc | 429 | |
b039eaaf SL |
430 | #[derive(Eq, PartialEq, Debug)] |
431 | struct NonCopy(i32); | |
432 | ||
c34b1796 | 433 | unsafe impl<T: Send> Send for Packet<T> {} |
1a4d82fc JJ |
434 | unsafe impl<T> Sync for Packet<T> {} |
435 | ||
436 | #[test] | |
437 | fn smoke() { | |
438 | let m = Mutex::new(()); | |
439 | drop(m.lock().unwrap()); | |
440 | drop(m.lock().unwrap()); | |
441 | } | |
442 | ||
443 | #[test] | |
444 | fn smoke_static() { | |
62682a34 | 445 | static M: StaticMutex = StaticMutex::new(); |
1a4d82fc JJ |
446 | unsafe { |
447 | drop(M.lock().unwrap()); | |
448 | drop(M.lock().unwrap()); | |
449 | M.destroy(); | |
450 | } | |
451 | } | |
452 | ||
453 | #[test] | |
454 | fn lots_and_lots() { | |
62682a34 | 455 | static M: StaticMutex = StaticMutex::new(); |
c34b1796 AL |
456 | static mut CNT: u32 = 0; |
457 | const J: u32 = 1000; | |
458 | const K: u32 = 3; | |
1a4d82fc JJ |
459 | |
460 | fn inc() { | |
85aaf69f | 461 | for _ in 0..J { |
1a4d82fc JJ |
462 | unsafe { |
463 | let _g = M.lock().unwrap(); | |
464 | CNT += 1; | |
465 | } | |
466 | } | |
467 | } | |
468 | ||
469 | let (tx, rx) = channel(); | |
85aaf69f | 470 | for _ in 0..K { |
1a4d82fc | 471 | let tx2 = tx.clone(); |
85aaf69f | 472 | thread::spawn(move|| { inc(); tx2.send(()).unwrap(); }); |
1a4d82fc | 473 | let tx2 = tx.clone(); |
85aaf69f | 474 | thread::spawn(move|| { inc(); tx2.send(()).unwrap(); }); |
1a4d82fc JJ |
475 | } |
476 | ||
477 | drop(tx); | |
85aaf69f | 478 | for _ in 0..2 * K { |
1a4d82fc JJ |
479 | rx.recv().unwrap(); |
480 | } | |
481 | assert_eq!(unsafe {CNT}, J * K * 2); | |
482 | unsafe { | |
483 | M.destroy(); | |
484 | } | |
485 | } | |
486 | ||
487 | #[test] | |
488 | fn try_lock() { | |
489 | let m = Mutex::new(()); | |
490 | *m.try_lock().unwrap() = (); | |
491 | } | |
492 | ||
b039eaaf SL |
493 | #[test] |
494 | fn test_into_inner() { | |
495 | let m = Mutex::new(NonCopy(10)); | |
496 | assert_eq!(m.into_inner().unwrap(), NonCopy(10)); | |
497 | } | |
498 | ||
499 | #[test] | |
500 | fn test_into_inner_drop() { | |
501 | struct Foo(Arc<AtomicUsize>); | |
502 | impl Drop for Foo { | |
503 | fn drop(&mut self) { | |
504 | self.0.fetch_add(1, Ordering::SeqCst); | |
505 | } | |
506 | } | |
507 | let num_drops = Arc::new(AtomicUsize::new(0)); | |
508 | let m = Mutex::new(Foo(num_drops.clone())); | |
509 | assert_eq!(num_drops.load(Ordering::SeqCst), 0); | |
510 | { | |
511 | let _inner = m.into_inner().unwrap(); | |
512 | assert_eq!(num_drops.load(Ordering::SeqCst), 0); | |
513 | } | |
514 | assert_eq!(num_drops.load(Ordering::SeqCst), 1); | |
515 | } | |
516 | ||
517 | #[test] | |
518 | fn test_into_inner_poison() { | |
519 | let m = Arc::new(Mutex::new(NonCopy(10))); | |
520 | let m2 = m.clone(); | |
521 | let _ = thread::spawn(move || { | |
522 | let _lock = m2.lock().unwrap(); | |
523 | panic!("test panic in inner thread to poison mutex"); | |
524 | }).join(); | |
525 | ||
526 | assert!(m.is_poisoned()); | |
527 | match Arc::try_unwrap(m).unwrap().into_inner() { | |
528 | Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), | |
529 | Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x), | |
530 | } | |
531 | } | |
532 | ||
533 | #[test] | |
534 | fn test_get_mut() { | |
535 | let mut m = Mutex::new(NonCopy(10)); | |
536 | *m.get_mut().unwrap() = NonCopy(20); | |
537 | assert_eq!(m.into_inner().unwrap(), NonCopy(20)); | |
538 | } | |
539 | ||
540 | #[test] | |
541 | fn test_get_mut_poison() { | |
542 | let m = Arc::new(Mutex::new(NonCopy(10))); | |
543 | let m2 = m.clone(); | |
544 | let _ = thread::spawn(move || { | |
545 | let _lock = m2.lock().unwrap(); | |
546 | panic!("test panic in inner thread to poison mutex"); | |
547 | }).join(); | |
548 | ||
549 | assert!(m.is_poisoned()); | |
550 | match Arc::try_unwrap(m).unwrap().get_mut() { | |
551 | Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), | |
552 | Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x), | |
553 | } | |
554 | } | |
555 | ||
1a4d82fc JJ |
556 | #[test] |
557 | fn test_mutex_arc_condvar() { | |
558 | let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); | |
559 | let packet2 = Packet(packet.0.clone()); | |
560 | let (tx, rx) = channel(); | |
85aaf69f | 561 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
562 | // wait until parent gets in |
563 | rx.recv().unwrap(); | |
564 | let &(ref lock, ref cvar) = &*packet2.0; | |
565 | let mut lock = lock.lock().unwrap(); | |
566 | *lock = true; | |
567 | cvar.notify_one(); | |
568 | }); | |
569 | ||
570 | let &(ref lock, ref cvar) = &*packet.0; | |
571 | let mut lock = lock.lock().unwrap(); | |
572 | tx.send(()).unwrap(); | |
573 | assert!(!*lock); | |
574 | while !*lock { | |
575 | lock = cvar.wait(lock).unwrap(); | |
576 | } | |
577 | } | |
578 | ||
579 | #[test] | |
580 | fn test_arc_condvar_poison() { | |
85aaf69f | 581 | let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); |
1a4d82fc JJ |
582 | let packet2 = Packet(packet.0.clone()); |
583 | let (tx, rx) = channel(); | |
584 | ||
85aaf69f | 585 | let _t = thread::spawn(move || -> () { |
1a4d82fc JJ |
586 | rx.recv().unwrap(); |
587 | let &(ref lock, ref cvar) = &*packet2.0; | |
588 | let _g = lock.lock().unwrap(); | |
589 | cvar.notify_one(); | |
590 | // Parent should fail when it wakes up. | |
591 | panic!(); | |
592 | }); | |
593 | ||
594 | let &(ref lock, ref cvar) = &*packet.0; | |
595 | let mut lock = lock.lock().unwrap(); | |
596 | tx.send(()).unwrap(); | |
597 | while *lock == 1 { | |
598 | match cvar.wait(lock) { | |
599 | Ok(l) => { | |
600 | lock = l; | |
601 | assert_eq!(*lock, 1); | |
602 | } | |
603 | Err(..) => break, | |
604 | } | |
605 | } | |
606 | } | |
607 | ||
608 | #[test] | |
609 | fn test_mutex_arc_poison() { | |
85aaf69f SL |
610 | let arc = Arc::new(Mutex::new(1)); |
611 | assert!(!arc.is_poisoned()); | |
1a4d82fc | 612 | let arc2 = arc.clone(); |
85aaf69f | 613 | let _ = thread::spawn(move|| { |
1a4d82fc JJ |
614 | let lock = arc2.lock().unwrap(); |
615 | assert_eq!(*lock, 2); | |
616 | }).join(); | |
617 | assert!(arc.lock().is_err()); | |
85aaf69f | 618 | assert!(arc.is_poisoned()); |
1a4d82fc JJ |
619 | } |
620 | ||
621 | #[test] | |
622 | fn test_mutex_arc_nested() { | |
623 | // Tests nested mutexes and access | |
624 | // to underlying data. | |
85aaf69f | 625 | let arc = Arc::new(Mutex::new(1)); |
1a4d82fc JJ |
626 | let arc2 = Arc::new(Mutex::new(arc)); |
627 | let (tx, rx) = channel(); | |
85aaf69f | 628 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
629 | let lock = arc2.lock().unwrap(); |
630 | let lock2 = lock.lock().unwrap(); | |
631 | assert_eq!(*lock2, 1); | |
632 | tx.send(()).unwrap(); | |
633 | }); | |
634 | rx.recv().unwrap(); | |
635 | } | |
636 | ||
637 | #[test] | |
638 | fn test_mutex_arc_access_in_unwind() { | |
85aaf69f | 639 | let arc = Arc::new(Mutex::new(1)); |
1a4d82fc | 640 | let arc2 = arc.clone(); |
85aaf69f | 641 | let _ = thread::spawn(move|| -> () { |
1a4d82fc | 642 | struct Unwinder { |
c34b1796 | 643 | i: Arc<Mutex<i32>>, |
1a4d82fc JJ |
644 | } |
645 | impl Drop for Unwinder { | |
646 | fn drop(&mut self) { | |
647 | *self.i.lock().unwrap() += 1; | |
648 | } | |
649 | } | |
650 | let _u = Unwinder { i: arc2 }; | |
651 | panic!(); | |
652 | }).join(); | |
653 | let lock = arc.lock().unwrap(); | |
654 | assert_eq!(*lock, 2); | |
655 | } | |
d9579d0f | 656 | |
b039eaaf SL |
657 | #[test] |
658 | fn test_mutex_unsized() { | |
659 | let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); | |
660 | { | |
661 | let b = &mut *mutex.lock().unwrap(); | |
662 | b[0] = 4; | |
663 | b[2] = 5; | |
664 | } | |
665 | let comp: &[i32] = &[4, 2, 5]; | |
666 | assert_eq!(&*mutex.lock().unwrap(), comp); | |
667 | } | |
1a4d82fc | 668 | } |