Skip to main content

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}