one_shot_mutex/unsync/
mutex.rs

1use core::cell::Cell;
2
3use lock_api::{GuardSend, RawMutex, RawMutexFair};
4
5/// A one-shot mutex that panics instead of (dead)locking on contention.
6///
7/// This mutex allows no contention and panics instead of blocking on [`lock`] if it is already locked.
8/// This is useful in situations where contention would be a bug,
9/// such as in single-threaded programs that would deadlock on contention.
10///
11/// This mutex does not implement `Sync`, which permits a slightly more efficient implementation.
12/// For a variant that does implement `Sync`, see [`sync::RawOneShotMutex`](crate::sync::RawOneShotMutex).
13///
14/// This mutex should be used through [`OneShotMutex`].
15///
16/// [`lock`]: Self::lock
17///
18/// # Examples
19///
20/// ```
21/// use one_shot_mutex::unsync::OneShotMutex;
22///
23/// let m: OneShotMutex<i32> = OneShotMutex::new(42);
24///
25/// // This is equivalent to `X.try_lock().unwrap()`.
26/// let x = m.lock();
27///
28/// // This panics instead of deadlocking.
29/// // let x2 = m.lock();
30///
31/// // Once we unlock the mutex, we can lock it again.
32/// drop(x);
33/// let x = m.lock();
34/// ```
35pub struct RawOneShotMutex {
36    lock: Cell<bool>,
37}
38
39impl RawOneShotMutex {
40    pub const fn new() -> Self {
41        Self::INIT
42    }
43}
44
45impl Default for RawOneShotMutex {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51unsafe impl RawMutex for RawOneShotMutex {
52    #[allow(clippy::declare_interior_mutable_const)]
53    const INIT: Self = Self {
54        lock: Cell::new(false),
55    };
56
57    type GuardMarker = GuardSend;
58
59    #[inline]
60    fn lock(&self) {
61        assert!(
62            self.try_lock(),
63            "called `lock` on a `RawOneShotMutex` that is already locked"
64        );
65    }
66
67    #[inline]
68    fn try_lock(&self) -> bool {
69        let was_locked = self.lock.replace(true);
70        !was_locked
71    }
72
73    #[inline]
74    unsafe fn unlock(&self) {
75        self.lock.set(false);
76    }
77
78    #[inline]
79    fn is_locked(&self) -> bool {
80        self.lock.get()
81    }
82}
83
84unsafe impl RawMutexFair for RawOneShotMutex {
85    #[inline]
86    unsafe fn unlock_fair(&self) {
87        unsafe { self.unlock() }
88    }
89
90    #[inline]
91    unsafe fn bump(&self) {}
92}
93
94/// A [`lock_api::Mutex`] based on [`RawOneShotMutex`].
95pub type OneShotMutex<T> = lock_api::Mutex<RawOneShotMutex, T>;
96
97/// A [`lock_api::MutexGuard`] based on [`RawOneShotMutex`].
98pub type OneShotMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawOneShotMutex, T>;
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn lock() {
106        let mutex = OneShotMutex::new(42);
107        let mut guard = mutex.lock();
108        assert_eq!(*guard, 42);
109
110        *guard += 1;
111        drop(guard);
112        let guard = mutex.lock();
113        assert_eq!(*guard, 43);
114    }
115
116    #[test]
117    #[should_panic]
118    fn lock_panic() {
119        let mutex = OneShotMutex::new(42);
120        let _guard = mutex.lock();
121        let _guard2 = mutex.lock();
122    }
123
124    #[test]
125    fn try_lock() {
126        let mutex = OneShotMutex::new(42);
127        let mut guard = mutex.try_lock().unwrap();
128        assert_eq!(*guard, 42);
129        assert!(mutex.try_lock().is_none());
130
131        *guard += 1;
132        drop(guard);
133        let guard = mutex.try_lock().unwrap();
134        assert_eq!(*guard, 43);
135    }
136}