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