Coverage Report

Created: 2026-05-24 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/spin-0.12.0/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, R = Spin> {
82
    inner: &'a SpinMutex<T, R>,
83
}
84
85
// SAFETY: Same unsafe impls as `std::sync::Mutex`
86
unsafe impl<T: ?Sized + Send, R> Sync for SpinMutex<T, R> {}
87
unsafe 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.
92
unsafe impl<T: ?Sized, R> Sync for SpinMutexGuard<'_, T, R> where for<'a> &'a mut T: Sync {}
93
unsafe impl<T: ?Sized, R> Send for SpinMutexGuard<'_, T, R> where for<'a> &'a mut T: Send {}
94
95
impl<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
163
impl<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
194
impl<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
323
impl<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
334
impl<T: Default, R> Default for SpinMutex<T, R> {
335
    fn default() -> Self {
336
        Self::new(Default::default())
337
    }
338
}
339
340
impl<T, R> From<T> for SpinMutex<T, R> {
341
    fn from(data: T) -> Self {
342
        Self::new(data)
343
    }
344
}
345
346
impl<'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
368
impl<'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
374
impl<'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
380
impl<'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
388
impl<'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
395
impl<'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
0
    fn drop(&mut self) {
398
0
        self.inner.lock.store(false, Ordering::Release);
399
0
    }
400
}
401
402
#[cfg(feature = "lock_api")]
403
unsafe 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)]
428
mod 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
}