/rust/registry/src/index.crates.io-1949cf8c6b5b557f/spin-0.9.8/src/mutex/spin.rs
Line | Count | Source |
1 | | //! A naïve spinning mutex. |
2 | | //! |
3 | | //! Waiting threads hammer an atomic variable until it becomes available. Best-case latency is low, but worst-case |
4 | | //! latency is theoretically infinite. |
5 | | |
6 | | use crate::{ |
7 | | atomic::{AtomicBool, Ordering}, |
8 | | RelaxStrategy, Spin, |
9 | | }; |
10 | | use core::{ |
11 | | cell::UnsafeCell, |
12 | | fmt, |
13 | | marker::PhantomData, |
14 | | mem::ManuallyDrop, |
15 | | ops::{Deref, DerefMut}, |
16 | | }; |
17 | | |
18 | | /// A [spin lock](https://en.m.wikipedia.org/wiki/Spinlock) providing mutually exclusive access to data. |
19 | | /// |
20 | | /// # Example |
21 | | /// |
22 | | /// ``` |
23 | | /// use spin; |
24 | | /// |
25 | | /// let lock = spin::mutex::SpinMutex::<_>::new(0); |
26 | | /// |
27 | | /// // Modify the data |
28 | | /// *lock.lock() = 2; |
29 | | /// |
30 | | /// // Read the data |
31 | | /// let answer = *lock.lock(); |
32 | | /// assert_eq!(answer, 2); |
33 | | /// ``` |
34 | | /// |
35 | | /// # Thread safety example |
36 | | /// |
37 | | /// ``` |
38 | | /// use spin; |
39 | | /// use std::sync::{Arc, Barrier}; |
40 | | /// |
41 | | /// let thread_count = 1000; |
42 | | /// let spin_mutex = Arc::new(spin::mutex::SpinMutex::<_>::new(0)); |
43 | | /// |
44 | | /// // We use a barrier to ensure the readout happens after all writing |
45 | | /// let barrier = Arc::new(Barrier::new(thread_count + 1)); |
46 | | /// |
47 | | /// # let mut ts = Vec::new(); |
48 | | /// for _ in (0..thread_count) { |
49 | | /// let my_barrier = barrier.clone(); |
50 | | /// let my_lock = spin_mutex.clone(); |
51 | | /// # let t = |
52 | | /// std::thread::spawn(move || { |
53 | | /// let mut guard = my_lock.lock(); |
54 | | /// *guard += 1; |
55 | | /// |
56 | | /// // Release the lock to prevent a deadlock |
57 | | /// drop(guard); |
58 | | /// my_barrier.wait(); |
59 | | /// }); |
60 | | /// # ts.push(t); |
61 | | /// } |
62 | | /// |
63 | | /// barrier.wait(); |
64 | | /// |
65 | | /// let answer = { *spin_mutex.lock() }; |
66 | | /// assert_eq!(answer, thread_count); |
67 | | /// |
68 | | /// # for t in ts { |
69 | | /// # t.join().unwrap(); |
70 | | /// # } |
71 | | /// ``` |
72 | | pub struct SpinMutex<T: ?Sized, R = Spin> { |
73 | | phantom: PhantomData<R>, |
74 | | pub(crate) lock: AtomicBool, |
75 | | data: UnsafeCell<T>, |
76 | | } |
77 | | |
78 | | /// A guard that provides mutable data access. |
79 | | /// |
80 | | /// When the guard falls out of scope it will release the lock. |
81 | | pub struct SpinMutexGuard<'a, T: ?Sized + 'a> { |
82 | | lock: &'a AtomicBool, |
83 | | data: *mut T, |
84 | | } |
85 | | |
86 | | // Same unsafe impls as `std::sync::Mutex` |
87 | | unsafe impl<T: ?Sized + Send, R> Sync for SpinMutex<T, R> {} |
88 | | unsafe impl<T: ?Sized + Send, R> Send for SpinMutex<T, R> {} |
89 | | |
90 | | unsafe impl<T: ?Sized + Sync> Sync for SpinMutexGuard<'_, T> {} |
91 | | unsafe impl<T: ?Sized + Send> Send for SpinMutexGuard<'_, T> {} |
92 | | |
93 | | impl<T, R> SpinMutex<T, R> { |
94 | | /// Creates a new [`SpinMutex`] wrapping the supplied data. |
95 | | /// |
96 | | /// # Example |
97 | | /// |
98 | | /// ``` |
99 | | /// use spin::mutex::SpinMutex; |
100 | | /// |
101 | | /// static MUTEX: SpinMutex<()> = SpinMutex::<_>::new(()); |
102 | | /// |
103 | | /// fn demo() { |
104 | | /// let lock = MUTEX.lock(); |
105 | | /// // do something with lock |
106 | | /// drop(lock); |
107 | | /// } |
108 | | /// ``` |
109 | | #[inline(always)] |
110 | 0 | pub const fn new(data: T) -> Self { |
111 | 0 | SpinMutex { |
112 | 0 | lock: AtomicBool::new(false), |
113 | 0 | data: UnsafeCell::new(data), |
114 | 0 | phantom: PhantomData, |
115 | 0 | } |
116 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutex<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>>>::new Unexecuted instantiation: <spin::mutex::spin::SpinMutex<_, _>>::new |
117 | | |
118 | | /// Consumes this [`SpinMutex`] and unwraps the underlying data. |
119 | | /// |
120 | | /// # Example |
121 | | /// |
122 | | /// ``` |
123 | | /// let lock = spin::mutex::SpinMutex::<_>::new(42); |
124 | | /// assert_eq!(42, lock.into_inner()); |
125 | | /// ``` |
126 | | #[inline(always)] |
127 | 0 | pub fn into_inner(self) -> T { |
128 | | // We know statically that there are no outstanding references to |
129 | | // `self` so there's no need to lock. |
130 | 0 | let SpinMutex { data, .. } = self; |
131 | 0 | data.into_inner() |
132 | 0 | } |
133 | | |
134 | | /// Returns a mutable pointer to the underlying data. |
135 | | /// |
136 | | /// This is mostly meant to be used for applications which require manual unlocking, but where |
137 | | /// storing both the lock and the pointer to the inner data gets inefficient. |
138 | | /// |
139 | | /// # Example |
140 | | /// ``` |
141 | | /// let lock = spin::mutex::SpinMutex::<_>::new(42); |
142 | | /// |
143 | | /// unsafe { |
144 | | /// core::mem::forget(lock.lock()); |
145 | | /// |
146 | | /// assert_eq!(lock.as_mut_ptr().read(), 42); |
147 | | /// lock.as_mut_ptr().write(58); |
148 | | /// |
149 | | /// lock.force_unlock(); |
150 | | /// } |
151 | | /// |
152 | | /// assert_eq!(*lock.lock(), 58); |
153 | | /// |
154 | | /// ``` |
155 | | #[inline(always)] |
156 | 0 | pub fn as_mut_ptr(&self) -> *mut T { |
157 | 0 | self.data.get() |
158 | 0 | } |
159 | | } |
160 | | |
161 | | impl<T: ?Sized, R: RelaxStrategy> SpinMutex<T, R> { |
162 | | /// Locks the [`SpinMutex`] and returns a guard that permits access to the inner data. |
163 | | /// |
164 | | /// The returned value may be dereferenced for data access |
165 | | /// and the lock will be dropped when the guard falls out of scope. |
166 | | /// |
167 | | /// ``` |
168 | | /// let lock = spin::mutex::SpinMutex::<_>::new(0); |
169 | | /// { |
170 | | /// let mut data = lock.lock(); |
171 | | /// // The lock is now locked and the data can be accessed |
172 | | /// *data += 1; |
173 | | /// // The lock is implicitly dropped at the end of the scope |
174 | | /// } |
175 | | /// ``` |
176 | | #[inline(always)] |
177 | 0 | pub fn lock(&self) -> SpinMutexGuard<T> { |
178 | | // Can fail to lock even if the spinlock is not locked. May be more efficient than `try_lock` |
179 | | // when called in a loop. |
180 | 0 | while self |
181 | 0 | .lock |
182 | 0 | .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) |
183 | 0 | .is_err() |
184 | | { |
185 | | // Wait until the lock looks unlocked before retrying |
186 | 0 | while self.is_locked() { |
187 | 0 | R::relax(); |
188 | 0 | } |
189 | | } |
190 | | |
191 | 0 | SpinMutexGuard { |
192 | 0 | lock: &self.lock, |
193 | 0 | data: unsafe { &mut *self.data.get() }, |
194 | 0 | } |
195 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutex<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>>>::lock Unexecuted instantiation: <spin::mutex::spin::SpinMutex<_, _>>::lock |
196 | | } |
197 | | |
198 | | impl<T: ?Sized, R> SpinMutex<T, R> { |
199 | | /// Returns `true` if the lock is currently held. |
200 | | /// |
201 | | /// # Safety |
202 | | /// |
203 | | /// This function provides no synchronization guarantees and so its result should be considered 'out of date' |
204 | | /// the instant it is called. Do not use it for synchronization purposes. However, it may be useful as a heuristic. |
205 | | #[inline(always)] |
206 | 0 | pub fn is_locked(&self) -> bool { |
207 | 0 | self.lock.load(Ordering::Relaxed) |
208 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutex<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>>>::is_locked Unexecuted instantiation: <spin::mutex::spin::SpinMutex<_, _>>::is_locked |
209 | | |
210 | | /// Force unlock this [`SpinMutex`]. |
211 | | /// |
212 | | /// # Safety |
213 | | /// |
214 | | /// This is *extremely* unsafe if the lock is not held by the current |
215 | | /// thread. However, this can be useful in some instances for exposing the |
216 | | /// lock to FFI that doesn't know how to deal with RAII. |
217 | | #[inline(always)] |
218 | 0 | pub unsafe fn force_unlock(&self) { |
219 | 0 | self.lock.store(false, Ordering::Release); |
220 | 0 | } |
221 | | |
222 | | /// Try to lock this [`SpinMutex`], returning a lock guard if successful. |
223 | | /// |
224 | | /// # Example |
225 | | /// |
226 | | /// ``` |
227 | | /// let lock = spin::mutex::SpinMutex::<_>::new(42); |
228 | | /// |
229 | | /// let maybe_guard = lock.try_lock(); |
230 | | /// assert!(maybe_guard.is_some()); |
231 | | /// |
232 | | /// // `maybe_guard` is still held, so the second call fails |
233 | | /// let maybe_guard2 = lock.try_lock(); |
234 | | /// assert!(maybe_guard2.is_none()); |
235 | | /// ``` |
236 | | #[inline(always)] |
237 | 0 | pub fn try_lock(&self) -> Option<SpinMutexGuard<T>> { |
238 | | // The reason for using a strong compare_exchange is explained here: |
239 | | // https://github.com/Amanieu/parking_lot/pull/207#issuecomment-575869107 |
240 | 0 | if self |
241 | 0 | .lock |
242 | 0 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) |
243 | 0 | .is_ok() |
244 | | { |
245 | 0 | Some(SpinMutexGuard { |
246 | 0 | lock: &self.lock, |
247 | 0 | data: unsafe { &mut *self.data.get() }, |
248 | 0 | }) |
249 | | } else { |
250 | 0 | None |
251 | | } |
252 | 0 | } |
253 | | |
254 | | /// Returns a mutable reference to the underlying data. |
255 | | /// |
256 | | /// Since this call borrows the [`SpinMutex`] mutably, and a mutable reference is guaranteed to be exclusive in |
257 | | /// Rust, no actual locking needs to take place -- the mutable borrow statically guarantees no locks exist. As |
258 | | /// such, this is a 'zero-cost' operation. |
259 | | /// |
260 | | /// # Example |
261 | | /// |
262 | | /// ``` |
263 | | /// let mut lock = spin::mutex::SpinMutex::<_>::new(0); |
264 | | /// *lock.get_mut() = 10; |
265 | | /// assert_eq!(*lock.lock(), 10); |
266 | | /// ``` |
267 | | #[inline(always)] |
268 | 0 | pub fn get_mut(&mut self) -> &mut T { |
269 | | // We know statically that there are no other references to `self`, so |
270 | | // there's no need to lock the inner mutex. |
271 | 0 | unsafe { &mut *self.data.get() } |
272 | 0 | } |
273 | | } |
274 | | |
275 | | impl<T: ?Sized + fmt::Debug, R> fmt::Debug for SpinMutex<T, R> { |
276 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
277 | 0 | match self.try_lock() { |
278 | 0 | Some(guard) => write!(f, "Mutex {{ data: ") |
279 | 0 | .and_then(|()| (&*guard).fmt(f)) |
280 | 0 | .and_then(|()| write!(f, "}}")), |
281 | 0 | None => write!(f, "Mutex {{ <locked> }}"), |
282 | | } |
283 | 0 | } |
284 | | } |
285 | | |
286 | | impl<T: ?Sized + Default, R> Default for SpinMutex<T, R> { |
287 | 0 | fn default() -> Self { |
288 | 0 | Self::new(Default::default()) |
289 | 0 | } |
290 | | } |
291 | | |
292 | | impl<T, R> From<T> for SpinMutex<T, R> { |
293 | 0 | fn from(data: T) -> Self { |
294 | 0 | Self::new(data) |
295 | 0 | } |
296 | | } |
297 | | |
298 | | impl<'a, T: ?Sized> SpinMutexGuard<'a, T> { |
299 | | /// Leak the lock guard, yielding a mutable reference to the underlying data. |
300 | | /// |
301 | | /// Note that this function will permanently lock the original [`SpinMutex`]. |
302 | | /// |
303 | | /// ``` |
304 | | /// let mylock = spin::mutex::SpinMutex::<_>::new(0); |
305 | | /// |
306 | | /// let data: &mut i32 = spin::mutex::SpinMutexGuard::leak(mylock.lock()); |
307 | | /// |
308 | | /// *data = 1; |
309 | | /// assert_eq!(*data, 1); |
310 | | /// ``` |
311 | | #[inline(always)] |
312 | 0 | pub fn leak(this: Self) -> &'a mut T { |
313 | | // Use ManuallyDrop to avoid stacked-borrow invalidation |
314 | 0 | let mut this = ManuallyDrop::new(this); |
315 | | // We know statically that only we are referencing data |
316 | 0 | unsafe { &mut *this.data } |
317 | 0 | } |
318 | | } |
319 | | |
320 | | impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for SpinMutexGuard<'a, T> { |
321 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
322 | 0 | fmt::Debug::fmt(&**self, f) |
323 | 0 | } |
324 | | } |
325 | | |
326 | | impl<'a, T: ?Sized + fmt::Display> fmt::Display for SpinMutexGuard<'a, T> { |
327 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
328 | 0 | fmt::Display::fmt(&**self, f) |
329 | 0 | } |
330 | | } |
331 | | |
332 | | impl<'a, T: ?Sized> Deref for SpinMutexGuard<'a, T> { |
333 | | type Target = T; |
334 | 0 | fn deref(&self) -> &T { |
335 | | // We know statically that only we are referencing data |
336 | 0 | unsafe { &*self.data } |
337 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>> as core::ops::deref::Deref>::deref Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<_> as core::ops::deref::Deref>::deref |
338 | | } |
339 | | |
340 | | impl<'a, T: ?Sized> DerefMut for SpinMutexGuard<'a, T> { |
341 | 0 | fn deref_mut(&mut self) -> &mut T { |
342 | | // We know statically that only we are referencing data |
343 | 0 | unsafe { &mut *self.data } |
344 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<dyn spdmlib::common::SpdmTransportEncap + core::marker::Sync + core::marker::Send> as core::ops::deref::DerefMut>::deref_mut Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>> as core::ops::deref::DerefMut>::deref_mut Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<_> as core::ops::deref::DerefMut>::deref_mut |
345 | | } |
346 | | |
347 | | impl<'a, T: ?Sized> Drop for SpinMutexGuard<'a, T> { |
348 | | /// The dropping of the MutexGuard will release the lock it was created from. |
349 | 0 | fn drop(&mut self) { |
350 | 0 | self.lock.store(false, Ordering::Release); |
351 | 0 | } Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<dyn spdmlib::common::SpdmDeviceIo + core::marker::Sync + core::marker::Send> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<alloc::collections::vec_deque::VecDeque<u8>> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<spdmlib::responder::context::ResponderContext> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<&mut [u8]> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<dyn spdmlib::common::SpdmTransportEncap + core::marker::Sync + core::marker::Send> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<alloc::collections::btree::map::BTreeMap<usize, alloc::boxed::Box<ring::digest::Context>>> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<_> as core::ops::drop::Drop>::drop Unexecuted instantiation: <spin::mutex::spin::SpinMutexGuard<fuzzlib::fake_device_io::FakeSpdmDeviceIoReceve> as core::ops::drop::Drop>::drop |
352 | | } |
353 | | |
354 | | #[cfg(feature = "lock_api")] |
355 | | unsafe impl<R: RelaxStrategy> lock_api_crate::RawMutex for SpinMutex<(), R> { |
356 | | type GuardMarker = lock_api_crate::GuardSend; |
357 | | |
358 | | const INIT: Self = Self::new(()); |
359 | | |
360 | 0 | fn lock(&self) { |
361 | | // Prevent guard destructor running |
362 | 0 | core::mem::forget(Self::lock(self)); |
363 | 0 | } |
364 | | |
365 | 0 | fn try_lock(&self) -> bool { |
366 | | // Prevent guard destructor running |
367 | 0 | Self::try_lock(self).map(core::mem::forget).is_some() |
368 | 0 | } |
369 | | |
370 | 0 | unsafe fn unlock(&self) { |
371 | 0 | self.force_unlock(); |
372 | 0 | } |
373 | | |
374 | 0 | fn is_locked(&self) -> bool { |
375 | 0 | Self::is_locked(self) |
376 | 0 | } |
377 | | } |
378 | | |
379 | | #[cfg(test)] |
380 | | mod tests { |
381 | | use std::prelude::v1::*; |
382 | | |
383 | | use std::sync::atomic::{AtomicUsize, Ordering}; |
384 | | use std::sync::mpsc::channel; |
385 | | use std::sync::Arc; |
386 | | use std::thread; |
387 | | |
388 | | type SpinMutex<T> = super::SpinMutex<T>; |
389 | | |
390 | | #[derive(Eq, PartialEq, Debug)] |
391 | | struct NonCopy(i32); |
392 | | |
393 | | #[test] |
394 | | fn smoke() { |
395 | | let m = SpinMutex::<_>::new(()); |
396 | | drop(m.lock()); |
397 | | drop(m.lock()); |
398 | | } |
399 | | |
400 | | #[test] |
401 | | fn lots_and_lots() { |
402 | | static M: SpinMutex<()> = SpinMutex::<_>::new(()); |
403 | | static mut CNT: u32 = 0; |
404 | | const J: u32 = 1000; |
405 | | const K: u32 = 3; |
406 | | |
407 | | fn inc() { |
408 | | for _ in 0..J { |
409 | | unsafe { |
410 | | let _g = M.lock(); |
411 | | CNT += 1; |
412 | | } |
413 | | } |
414 | | } |
415 | | |
416 | | let (tx, rx) = channel(); |
417 | | let mut ts = Vec::new(); |
418 | | for _ in 0..K { |
419 | | let tx2 = tx.clone(); |
420 | | ts.push(thread::spawn(move || { |
421 | | inc(); |
422 | | tx2.send(()).unwrap(); |
423 | | })); |
424 | | let tx2 = tx.clone(); |
425 | | ts.push(thread::spawn(move || { |
426 | | inc(); |
427 | | tx2.send(()).unwrap(); |
428 | | })); |
429 | | } |
430 | | |
431 | | drop(tx); |
432 | | for _ in 0..2 * K { |
433 | | rx.recv().unwrap(); |
434 | | } |
435 | | assert_eq!(unsafe { CNT }, J * K * 2); |
436 | | |
437 | | for t in ts { |
438 | | t.join().unwrap(); |
439 | | } |
440 | | } |
441 | | |
442 | | #[test] |
443 | | fn try_lock() { |
444 | | let mutex = SpinMutex::<_>::new(42); |
445 | | |
446 | | // First lock succeeds |
447 | | let a = mutex.try_lock(); |
448 | | assert_eq!(a.as_ref().map(|r| **r), Some(42)); |
449 | | |
450 | | // Additional lock fails |
451 | | let b = mutex.try_lock(); |
452 | | assert!(b.is_none()); |
453 | | |
454 | | // After dropping lock, it succeeds again |
455 | | ::core::mem::drop(a); |
456 | | let c = mutex.try_lock(); |
457 | | assert_eq!(c.as_ref().map(|r| **r), Some(42)); |
458 | | } |
459 | | |
460 | | #[test] |
461 | | fn test_into_inner() { |
462 | | let m = SpinMutex::<_>::new(NonCopy(10)); |
463 | | assert_eq!(m.into_inner(), NonCopy(10)); |
464 | | } |
465 | | |
466 | | #[test] |
467 | | fn test_into_inner_drop() { |
468 | | struct Foo(Arc<AtomicUsize>); |
469 | | impl Drop for Foo { |
470 | | fn drop(&mut self) { |
471 | | self.0.fetch_add(1, Ordering::SeqCst); |
472 | | } |
473 | | } |
474 | | let num_drops = Arc::new(AtomicUsize::new(0)); |
475 | | let m = SpinMutex::<_>::new(Foo(num_drops.clone())); |
476 | | assert_eq!(num_drops.load(Ordering::SeqCst), 0); |
477 | | { |
478 | | let _inner = m.into_inner(); |
479 | | assert_eq!(num_drops.load(Ordering::SeqCst), 0); |
480 | | } |
481 | | assert_eq!(num_drops.load(Ordering::SeqCst), 1); |
482 | | } |
483 | | |
484 | | #[test] |
485 | | fn test_mutex_arc_nested() { |
486 | | // Tests nested mutexes and access |
487 | | // to underlying data. |
488 | | let arc = Arc::new(SpinMutex::<_>::new(1)); |
489 | | let arc2 = Arc::new(SpinMutex::<_>::new(arc)); |
490 | | let (tx, rx) = channel(); |
491 | | let t = thread::spawn(move || { |
492 | | let lock = arc2.lock(); |
493 | | let lock2 = lock.lock(); |
494 | | assert_eq!(*lock2, 1); |
495 | | tx.send(()).unwrap(); |
496 | | }); |
497 | | rx.recv().unwrap(); |
498 | | t.join().unwrap(); |
499 | | } |
500 | | |
501 | | #[test] |
502 | | fn test_mutex_arc_access_in_unwind() { |
503 | | let arc = Arc::new(SpinMutex::<_>::new(1)); |
504 | | let arc2 = arc.clone(); |
505 | | let _ = thread::spawn(move || -> () { |
506 | | struct Unwinder { |
507 | | i: Arc<SpinMutex<i32>>, |
508 | | } |
509 | | impl Drop for Unwinder { |
510 | | fn drop(&mut self) { |
511 | | *self.i.lock() += 1; |
512 | | } |
513 | | } |
514 | | let _u = Unwinder { i: arc2 }; |
515 | | panic!(); |
516 | | }) |
517 | | .join(); |
518 | | let lock = arc.lock(); |
519 | | assert_eq!(*lock, 2); |
520 | | } |
521 | | |
522 | | #[test] |
523 | | fn test_mutex_unsized() { |
524 | | let mutex: &SpinMutex<[i32]> = &SpinMutex::<_>::new([1, 2, 3]); |
525 | | { |
526 | | let b = &mut *mutex.lock(); |
527 | | b[0] = 4; |
528 | | b[2] = 5; |
529 | | } |
530 | | let comp: &[i32] = &[4, 2, 5]; |
531 | | assert_eq!(&*mutex.lock(), comp); |
532 | | } |
533 | | |
534 | | #[test] |
535 | | fn test_mutex_force_lock() { |
536 | | let lock = SpinMutex::<_>::new(()); |
537 | | ::std::mem::forget(lock.lock()); |
538 | | unsafe { |
539 | | lock.force_unlock(); |
540 | | } |
541 | | assert!(lock.try_lock().is_some()); |
542 | | } |
543 | | } |