spin/mutex/spin.rs
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
6use crate::{
7 atomic::{AtomicBool, Ordering},
8 RelaxStrategy, Spin,
9};
10use 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/// ```
72pub 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.
81pub struct SpinMutexGuard<'a, T: ?Sized + 'a, R = Spin> {
82 inner: &'a SpinMutex<T, R>,
83}
84
85// SAFETY: Same unsafe impls as `std::sync::Mutex`
86unsafe impl<T: ?Sized + Send, R> Sync for SpinMutex<T, R> {}
87unsafe impl<T: ?Sized + Send, R> Send for SpinMutex<T, R> {}
88
89// SAFETY: Mutex guards can be thought of as mutable reference to the inner data. In fact, this
90// would be their ideal representation if it were not for the need for the critical section to end
91// *after* the reference is no longer live.
92unsafe impl<T: ?Sized, R> Sync for SpinMutexGuard<'_, T, R> where for<'a> &'a mut T: Sync {}
93unsafe impl<T: ?Sized, R> Send for SpinMutexGuard<'_, T, R> where for<'a> &'a mut T: Send {}
94
95impl<T, R> SpinMutex<T, R> {
96 /// Creates a new [`SpinMutex`] wrapping the supplied data.
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// use spin::mutex::SpinMutex;
102 ///
103 /// static MUTEX: SpinMutex<()> = SpinMutex::<_>::new(());
104 ///
105 /// fn demo() {
106 /// let lock = MUTEX.lock();
107 /// // do something with lock
108 /// drop(lock);
109 /// }
110 /// ```
111 #[inline(always)]
112 pub const fn new(data: T) -> Self {
113 SpinMutex {
114 lock: AtomicBool::new(false),
115 data: UnsafeCell::new(data),
116 phantom: PhantomData,
117 }
118 }
119
120 /// Consumes this [`SpinMutex`] and unwraps the underlying data.
121 ///
122 /// # Example
123 ///
124 /// ```
125 /// let lock = spin::mutex::SpinMutex::<_>::new(42);
126 /// assert_eq!(42, lock.into_inner());
127 /// ```
128 #[inline(always)]
129 pub fn into_inner(self) -> T {
130 // We know statically that there are no outstanding references to
131 // `self` so there's no need to lock.
132 let SpinMutex { data, .. } = self;
133 data.into_inner()
134 }
135
136 /// Returns a mutable pointer to the underlying data.
137 ///
138 /// This is mostly meant to be used for applications which require manual unlocking, but where
139 /// storing both the lock and the pointer to the inner data gets inefficient.
140 ///
141 /// # Example
142 /// ```
143 /// let lock = spin::mutex::SpinMutex::<_>::new(42);
144 ///
145 /// unsafe {
146 /// core::mem::forget(lock.lock());
147 ///
148 /// assert_eq!(lock.as_mut_ptr().read(), 42);
149 /// lock.as_mut_ptr().write(58);
150 ///
151 /// lock.force_unlock();
152 /// }
153 ///
154 /// assert_eq!(*lock.lock(), 58);
155 ///
156 /// ```
157 #[inline(always)]
158 pub fn as_mut_ptr(&self) -> *mut T {
159 self.data.get()
160 }
161}
162
163impl<T: ?Sized, R: RelaxStrategy> SpinMutex<T, R> {
164 /// Locks the [`SpinMutex`] and returns a guard that permits access to the inner data.
165 ///
166 /// The returned value may be dereferenced for data access
167 /// and the lock will be dropped when the guard falls out of scope.
168 ///
169 /// ```
170 /// let lock = spin::mutex::SpinMutex::<_>::new(0);
171 /// {
172 /// let mut data = lock.lock();
173 /// // The lock is now locked and the data can be accessed
174 /// *data += 1;
175 /// // The lock is implicitly dropped at the end of the scope
176 /// }
177 /// ```
178 #[inline(always)]
179 pub fn lock(&self) -> SpinMutexGuard<'_, T, R> {
180 // Can fail to lock even if the spinlock is not locked. May be more efficient than `try_lock`
181 // when called in a loop.
182 loop {
183 if let Some(guard) = self.try_lock_weak() {
184 break guard;
185 }
186
187 while self.is_locked() {
188 R::relax();
189 }
190 }
191 }
192}
193
194impl<T: ?Sized, R> SpinMutex<T, R> {
195 /// Returns `true` if the lock is currently held.
196 ///
197 /// # Safety
198 ///
199 /// This function provides no synchronization guarantees and so its result should be considered 'out of date'
200 /// the instant it is called. Do not use it for synchronization purposes. However, it may be useful as a heuristic.
201 #[inline(always)]
202 pub fn is_locked(&self) -> bool {
203 self.lock.load(Ordering::Relaxed)
204 }
205
206 /// Force unlock this [`SpinMutex`].
207 ///
208 /// # Safety
209 ///
210 /// This is *extremely* unsafe if the lock is not held by the current
211 /// thread. However, this can be useful in some instances for exposing the
212 /// lock to FFI that doesn't know how to deal with RAII.
213 #[inline(always)]
214 pub unsafe fn force_unlock(&self) {
215 self.lock.store(false, Ordering::Release);
216 }
217
218 /// Try to lock this [`SpinMutex`], returning a lock guard if successful.
219 ///
220 /// # Example
221 ///
222 /// ```
223 /// let lock = spin::mutex::SpinMutex::<_>::new(42);
224 ///
225 /// let maybe_guard = lock.try_lock();
226 /// assert!(maybe_guard.is_some());
227 ///
228 /// // `maybe_guard` is still held, so the second call fails
229 /// let maybe_guard2 = lock.try_lock();
230 /// assert!(maybe_guard2.is_none());
231 /// ```
232 #[inline(always)]
233 pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T, R>> {
234 // The reason for using a strong compare_exchange is explained here:
235 // https://github.com/Amanieu/parking_lot/pull/207#issuecomment-575869107
236 //
237 // See also the giant comment about Ordering::Acquire in try_lock_weak below.
238 if self
239 .lock
240 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
241 .is_ok()
242 {
243 Some(SpinMutexGuard { inner: self })
244 } else {
245 None
246 }
247 }
248
249 /// Try to lock this [`SpinMutex`], returning a lock guard if successful.
250 ///
251 /// Unlike [`SpinMutex::try_lock`], this function is allowed to spuriously fail even when the mutex is unlocked,
252 /// which can result in more efficient code on some platforms.
253 #[inline(always)]
254 pub fn try_lock_weak(&self) -> Option<SpinMutexGuard<'_, T, R>> {
255 // There's some debate about whether Ordering::Acquire is sufficient here. In one
256 // interpretation of the the C++ standard, a lock operation like this could be re-ordered
257 // with a prior unlock of a different mutex. In other words this...
258 //
259 // let a_guard = A.lock();
260 // // do stuff...
261 // drop(a_guard);
262 // let b_guard = B.lock();
263 // // do more stuff...
264 // drop(b_guard);
265 //
266 // ... could be reordered by the compiler into this...
267 //
268 // let a_guard = A.lock();
269 // let b_guard = B.lock();
270 // // do stuff...
271 // // do more stuff...
272 // drop(a_guard);
273 // drop(b_guard);
274 //
275 // ...because both the store-release in `drop(a_guard)` and the load-acquire in `B.lock()`
276 // allow this code movement. (Using Ordering::AcqRel here instead would forbid this,
277 // because nothing can move down across a store-release, but it would also prevent valid
278 // optimizations.) The worry is that this could lead to deadlocks in arguably correct
279 // programs, for example one thread locking A-then-B while another thread locks B-then-A,
280 // even though a straight-line reading of the code says that can't happen.
281 //
282 // However, there's another interpretation of the standard that says this reordering is
283 // illegal. The idea is that even though moving a (non-sequentially-consistent)
284 // store-release down across a load-acquire is ok, moving it down across an *unbounded
285 // loop* violates the requirement that atomic stores should be visible to other threads in
286 // a "finite period of time": https://eel.is/c++draft/basic#intro.progress-18. Concretely,
287 // `try_lock` or `try_lock_weak` could be reordered like this (not a problem?), but `lock`
288 // with its internal loop can't be. This seems to be how compilers currently behave, in
289 // any case. See also:
290 // - https://preshing.com/20170612/can-reordering-of-release-acquire-operations-introduce-deadlock
291 // - https://x.com/tvaneerd/status/1258426442649657346
292 // - https://youtu.be/A8eCGOqgvH4?t=2551
293 if self
294 .lock
295 .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
296 .is_ok()
297 {
298 Some(SpinMutexGuard { inner: self })
299 } else {
300 None
301 }
302 }
303
304 /// Returns a mutable reference to the underlying data.
305 ///
306 /// Since this call borrows the [`SpinMutex`] mutably, and a mutable reference is guaranteed to be exclusive in
307 /// Rust, no actual locking needs to take place -- the mutable borrow statically guarantees no locks exist. As
308 /// such, this is a 'zero-cost' operation.
309 ///
310 /// # Example
311 ///
312 /// ```
313 /// let mut lock = spin::mutex::SpinMutex::<_>::new(0);
314 /// *lock.get_mut() = 10;
315 /// assert_eq!(*lock.lock(), 10);
316 /// ```
317 #[inline(always)]
318 pub fn get_mut(&mut self) -> &mut T {
319 self.data.get_mut()
320 }
321}
322
323impl<T: ?Sized + fmt::Debug, R> fmt::Debug for SpinMutex<T, R> {
324 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325 match self.try_lock() {
326 Some(guard) => write!(f, "Mutex {{ data: ")
327 .and_then(|()| (*guard).fmt(f))
328 .and_then(|()| write!(f, " }}")),
329 None => write!(f, "Mutex {{ <locked> }}"),
330 }
331 }
332}
333
334impl<T: Default, R> Default for SpinMutex<T, R> {
335 fn default() -> Self {
336 Self::new(Default::default())
337 }
338}
339
340impl<T, R> From<T> for SpinMutex<T, R> {
341 fn from(data: T) -> Self {
342 Self::new(data)
343 }
344}
345
346impl<'a, T: ?Sized, R> SpinMutexGuard<'a, T, R> {
347 /// Leak the lock guard, yielding a mutable reference to the underlying data.
348 ///
349 /// Note that this function will permanently lock the original [`SpinMutex`].
350 ///
351 /// ```
352 /// let mylock = spin::mutex::SpinMutex::<_>::new(0);
353 ///
354 /// let data: &mut i32 = spin::mutex::SpinMutexGuard::leak(mylock.lock());
355 ///
356 /// *data = 1;
357 /// assert_eq!(*data, 1);
358 /// ```
359 #[inline(always)]
360 pub fn leak(this: Self) -> &'a mut T {
361 // Use ManuallyDrop to avoid stacked-borrow invalidation
362 let this = ManuallyDrop::new(this);
363 // We know statically that only we are referencing data
364 unsafe { &mut *this.inner.data.get() }
365 }
366}
367
368impl<'a, T: ?Sized + fmt::Debug, R> fmt::Debug for SpinMutexGuard<'a, T, R> {
369 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370 fmt::Debug::fmt(&**self, f)
371 }
372}
373
374impl<'a, T: ?Sized + fmt::Display, R> fmt::Display for SpinMutexGuard<'a, T, R> {
375 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
376 fmt::Display::fmt(&**self, f)
377 }
378}
379
380impl<'a, T: ?Sized, R> Deref for SpinMutexGuard<'a, T, R> {
381 type Target = T;
382 fn deref(&self) -> &T {
383 // We know statically that only we are referencing data
384 unsafe { &*self.inner.data.get() }
385 }
386}
387
388impl<'a, T: ?Sized, R> DerefMut for SpinMutexGuard<'a, T, R> {
389 fn deref_mut(&mut self) -> &mut T {
390 // We know statically that only we are referencing data
391 unsafe { &mut *self.inner.data.get() }
392 }
393}
394
395impl<'a, T: ?Sized, R> Drop for SpinMutexGuard<'a, T, R> {
396 /// The dropping of the MutexGuard will release the lock it was created from.
397 fn drop(&mut self) {
398 self.inner.lock.store(false, Ordering::Release);
399 }
400}
401
402#[cfg(feature = "lock_api")]
403unsafe impl<R: RelaxStrategy> lock_api_crate::RawMutex for SpinMutex<(), R> {
404 type GuardMarker = lock_api_crate::GuardSend;
405
406 const INIT: Self = Self::new(());
407
408 fn lock(&self) {
409 // Prevent guard destructor running
410 core::mem::forget(Self::lock(self));
411 }
412
413 fn try_lock(&self) -> bool {
414 // Prevent guard destructor running
415 Self::try_lock(self).map(core::mem::forget).is_some()
416 }
417
418 unsafe fn unlock(&self) {
419 self.force_unlock();
420 }
421
422 fn is_locked(&self) -> bool {
423 Self::is_locked(self)
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use std::prelude::v1::*;
430
431 use std::sync::atomic::{AtomicUsize, Ordering};
432 use std::sync::mpsc::channel;
433 use std::sync::Arc;
434 use std::thread;
435
436 type SpinMutex<T> = super::SpinMutex<T>;
437
438 #[derive(Eq, PartialEq, Debug)]
439 struct NonCopy(i32);
440
441 #[test]
442 fn smoke() {
443 let m = SpinMutex::<_>::new(());
444 drop(m.lock());
445 drop(m.lock());
446 }
447
448 #[test]
449 fn lots_and_lots() {
450 static M: SpinMutex<()> = SpinMutex::<_>::new(());
451 static mut CNT: u32 = 0;
452 const J: u32 = 1000;
453 const K: u32 = 3;
454
455 fn inc() {
456 for _ in 0..J {
457 unsafe {
458 let _g = M.lock();
459 CNT += 1;
460 }
461 }
462 }
463
464 let (tx, rx) = channel();
465 let mut ts = Vec::new();
466 for _ in 0..K {
467 let tx2 = tx.clone();
468 ts.push(thread::spawn(move || {
469 inc();
470 tx2.send(()).unwrap();
471 }));
472 let tx2 = tx.clone();
473 ts.push(thread::spawn(move || {
474 inc();
475 tx2.send(()).unwrap();
476 }));
477 }
478
479 drop(tx);
480 for _ in 0..2 * K {
481 rx.recv().unwrap();
482 }
483 assert_eq!(unsafe { CNT }, J * K * 2);
484
485 for t in ts {
486 t.join().unwrap();
487 }
488 }
489
490 #[test]
491 fn try_lock() {
492 let mutex = SpinMutex::<_>::new(42);
493
494 // First lock succeeds
495 let a = mutex.try_lock();
496 assert_eq!(a.as_ref().map(|r| **r), Some(42));
497
498 // Additional lock fails
499 let b = mutex.try_lock();
500 assert!(b.is_none());
501
502 // After dropping lock, it succeeds again
503 ::core::mem::drop(a);
504 let c = mutex.try_lock();
505 assert_eq!(c.as_ref().map(|r| **r), Some(42));
506 }
507
508 #[test]
509 fn test_into_inner() {
510 let m = SpinMutex::<_>::new(NonCopy(10));
511 assert_eq!(m.into_inner(), NonCopy(10));
512 }
513
514 #[test]
515 fn test_into_inner_drop() {
516 struct Foo(Arc<AtomicUsize>);
517 impl Drop for Foo {
518 fn drop(&mut self) {
519 self.0.fetch_add(1, Ordering::SeqCst);
520 }
521 }
522 let num_drops = Arc::new(AtomicUsize::new(0));
523 let m = SpinMutex::<_>::new(Foo(num_drops.clone()));
524 assert_eq!(num_drops.load(Ordering::SeqCst), 0);
525 {
526 let _inner = m.into_inner();
527 assert_eq!(num_drops.load(Ordering::SeqCst), 0);
528 }
529 assert_eq!(num_drops.load(Ordering::SeqCst), 1);
530 }
531
532 #[test]
533 fn test_mutex_arc_nested() {
534 // Tests nested mutexes and access
535 // to underlying data.
536 let arc = Arc::new(SpinMutex::<_>::new(1));
537 let arc2 = Arc::new(SpinMutex::<_>::new(arc));
538 let (tx, rx) = channel();
539 let t = thread::spawn(move || {
540 let lock = arc2.lock();
541 let lock2 = lock.lock();
542 assert_eq!(*lock2, 1);
543 tx.send(()).unwrap();
544 });
545 rx.recv().unwrap();
546 t.join().unwrap();
547 }
548
549 #[test]
550 fn test_mutex_arc_access_in_unwind() {
551 let arc = Arc::new(SpinMutex::<_>::new(1));
552 let arc2 = arc.clone();
553 let _ = thread::spawn(move || -> () {
554 struct Unwinder {
555 i: Arc<SpinMutex<i32>>,
556 }
557 impl Drop for Unwinder {
558 fn drop(&mut self) {
559 *self.i.lock() += 1;
560 }
561 }
562 let _u = Unwinder { i: arc2 };
563 panic!();
564 })
565 .join();
566 let lock = arc.lock();
567 assert_eq!(*lock, 2);
568 }
569
570 #[test]
571 fn test_mutex_unsized() {
572 let mutex: &SpinMutex<[i32]> = &SpinMutex::<_>::new([1, 2, 3]);
573 {
574 let b = &mut *mutex.lock();
575 b[0] = 4;
576 b[2] = 5;
577 }
578 let comp: &[i32] = &[4, 2, 5];
579 assert_eq!(&*mutex.lock(), comp);
580 }
581
582 #[test]
583 fn test_mutex_force_lock() {
584 let lock = SpinMutex::<_>::new(());
585 ::std::mem::forget(lock.lock());
586 unsafe {
587 lock.force_unlock();
588 }
589 assert!(lock.try_lock().is_some());
590 }
591}