Skip to main content

shuttle_std/sync/
mutex.rs

1use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult};
2use crate::sync::{ResourceSignature, ResourceType};
3use shuttle_engine::current;
4use shuttle_engine::future::batch_semaphore::{BatchSemaphore, Fairness};
5use shuttle_engine::runtime::execution::ExecutionState;
6use shuttle_engine::runtime::task::TaskId;
7use std::cell::RefCell;
8use std::fmt::{Debug, Display};
9use std::ops::{Deref, DerefMut};
10use std::panic::{RefUnwindSafe, UnwindSafe};
11use tracing::trace;
12
13/// A mutex, the same as [`std::sync::Mutex`].
14pub struct Mutex<T: ?Sized> {
15    state: RefCell<MutexState>,
16    semaphore: BatchSemaphore,
17    inner: std::sync::Mutex<T>,
18}
19
20/// A mutex guard, the same as [`std::sync::MutexGuard`].
21pub struct MutexGuard<'a, T: ?Sized> {
22    inner: Option<std::sync::MutexGuard<'a, T>>,
23    mutex: &'a Mutex<T>,
24}
25
26#[derive(Debug)]
27struct MutexState {
28    holder: Option<TaskId>,
29}
30
31impl<T> Mutex<T> {
32    /// Creates a new mutex in an unlocked state ready for use.
33    #[track_caller]
34    pub const fn new(value: T) -> Self {
35        Self::new_internal(value, ResourceSignature::new_const(ResourceType::Mutex))
36    }
37
38    pub(crate) const fn new_internal(value: T, signature: ResourceSignature) -> Self {
39        let state = MutexState { holder: None };
40        Self {
41            state: RefCell::new(state),
42            semaphore: BatchSemaphore::const_new_with_signature(1, Fairness::Unfair, signature),
43            inner: std::sync::Mutex::new(value),
44        }
45    }
46}
47
48impl<T: ?Sized> Mutex<T> {
49    /// Acquires a mutex, blocking the current thread until it is able to do so.
50    pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
51        let me = current::me();
52
53        let mut state = self.state.borrow_mut();
54        trace!(holder=?state.holder, semaphore=?self.semaphore, "waiting to acquire mutex {:p}", self);
55        drop(state);
56
57        // Detect deadlock due to re-entrancy.
58        state = self.state.borrow_mut();
59        assert!(
60            match &state.holder {
61                Some(holder) => *holder != me,
62                None => true,
63            },
64            "deadlock! task {me:?} tried to acquire a Mutex it already holds"
65        );
66        drop(state);
67
68        // The first poll of the acquire is the scheduling point before the lock operation. The
69        // acquire fails only if the semaphore is closed, which happens only if a release ran after
70        // the execution stopped (see `BatchSemaphore::release`). In that case, the lock is taken as
71        // if it were free, and the inner `std::sync::Mutex` reports whether it is poisoned.
72        let _ = self.semaphore.acquire_blocking(1);
73
74        state = self.state.borrow_mut();
75        assert!(state.holder.is_none());
76        state.holder = Some(me);
77        drop(state);
78
79        trace!(semaphore=?self.semaphore, "acquired mutex {:p}", self);
80
81        // Grab a `MutexGuard` from the inner lock, which we must be able to acquire here
82        let result = match self.inner.try_lock() {
83            Ok(guard) => Ok(MutexGuard {
84                inner: Some(guard),
85                mutex: self,
86            }),
87            Err(TryLockError::Poisoned(guard)) => Err(PoisonError::new(MutexGuard {
88                inner: Some(guard.into_inner()),
89                mutex: self,
90            })),
91            Err(TryLockError::WouldBlock) => unreachable!("mutex state out of sync"),
92        };
93
94        result
95    }
96
97    /// Attempts to acquire this lock.
98    ///
99    /// If the lock could not be acquired at this time, then Err is returned. This function does not
100    /// block.
101    pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
102        let me = current::me();
103
104        let mut state = self.state.borrow_mut();
105        trace!(holder=?state.holder, semaphore=?self.semaphore, "trying to acquire mutex {:p}", self);
106        drop(state);
107
108        // `try_acquire` is a yield point. We need to let other threads in here so they
109        // (a) may fail a `try_lock` (in case we acquired), or
110        // (b) may release the lock (in case we failed to acquire) so we can succeed in a subsequent `try_lock`.
111        self.semaphore.try_acquire(1).map_err(|_| TryLockError::WouldBlock)?;
112
113        state = self.state.borrow_mut();
114        state.holder = Some(me);
115        drop(state);
116
117        trace!(semaphore=?self.semaphore, "acquired mutex {:p}", self);
118
119        // Grab a `MutexGuard` from the inner lock, which we must be able to acquire here
120        let result = match self.inner.try_lock() {
121            Ok(guard) => Ok(MutexGuard {
122                inner: Some(guard),
123                mutex: self,
124            }),
125            Err(TryLockError::Poisoned(guard)) => Err(TryLockError::Poisoned(PoisonError::new(MutexGuard {
126                inner: Some(guard.into_inner()),
127                mutex: self,
128            }))),
129            Err(TryLockError::WouldBlock) => unreachable!("mutex state out of sync"),
130        };
131
132        result
133    }
134
135    /// Returns a mutable reference to the underlying data.
136    ///
137    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
138    /// take place -- the mutable borrow statically guarantees no locks exist.
139    #[inline]
140    pub fn get_mut(&mut self) -> LockResult<&mut T> {
141        self.inner.get_mut()
142    }
143
144    /// Consumes this mutex, returning the underlying data.
145    pub fn into_inner(self) -> LockResult<T>
146    where
147        T: Sized,
148    {
149        let state = self.state.borrow();
150        assert!(state.holder.is_none());
151
152        // Update the receiver's clock with the Mutex clock
153        self.semaphore.try_acquire(1).unwrap();
154
155        self.inner.into_inner()
156    }
157
158    /// Clear the poisoned state from a mutex.
159    pub fn clear_poison(&self) {
160        self.inner.clear_poison()
161    }
162}
163
164// Safety: Mutex is never actually passed across true threads, only across continuations. The
165// Rc<RefCell<_>> type therefore can't be preempted mid-bookkeeping-operation.
166// TODO we shouldn't need to do this, but RefCell is not Send, and anything we put within a Mutex
167// TODO needs to be Send.
168unsafe impl<T: Send + ?Sized> Send for Mutex<T> {}
169unsafe impl<T: Send + ?Sized> Sync for Mutex<T> {}
170
171// TODO this is the RefCell biting us again
172impl<T: ?Sized> UnwindSafe for Mutex<T> {}
173impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
174
175impl<T: Default> Default for Mutex<T> {
176    fn default() -> Self {
177        Self::new(Default::default())
178    }
179}
180
181impl<T: ?Sized + Debug> Debug for Mutex<T> {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        Debug::fmt(&self.inner, f)
184    }
185}
186
187impl<'a, T: ?Sized> MutexGuard<'a, T> {
188    /// Release the lock, but return a reference to it so it can be re-acquired later
189    pub(super) fn unlock(self) -> &'a Mutex<T> {
190        self.mutex
191    }
192}
193
194impl<T: ?Sized> Drop for MutexGuard<'_, T> {
195    fn drop(&mut self) {
196        // Execution teardown can unwind the task from the yield point in `release` (see
197        // `ExecutionState::tear_down`). `release` releases the permit even then, and the rest of
198        // unlocking has to happen too, so that the destructors that teardown runs later can lock the
199        // mutex.
200        struct Unlock<'g, 'a, T: ?Sized>(&'g mut MutexGuard<'a, T>);
201        impl<T: ?Sized> Drop for Unlock<'_, '_, T> {
202            #[inline]
203            fn drop(&mut self) {
204                let guard = &mut *self.0;
205                // Release the inner mutex. Teardown unwinding the task is no panic, which mustn't
206                // poison it.
207                let clear_poison = std::thread::panicking()
208                    && !guard.mutex.inner.is_poisoned()
209                    && ExecutionState::unwinding_for_teardown();
210                guard.inner = None;
211                if clear_poison {
212                    guard.mutex.inner.clear_poison();
213                }
214
215                let mut state = guard.mutex.state.borrow_mut();
216                trace!(semaphore=?guard.mutex.semaphore, "releasing mutex {:p}", guard.mutex);
217                state.holder = None;
218            }
219        }
220        let unlock = Unlock(self);
221
222        // Release a permit (this is a yield point)
223        unlock.0.mutex.semaphore.release(1);
224    }
225}
226
227impl<T: ?Sized> Deref for MutexGuard<'_, T> {
228    type Target = T;
229
230    fn deref(&self) -> &Self::Target {
231        self.inner.as_ref().unwrap()
232    }
233}
234
235impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
236    fn deref_mut(&mut self) -> &mut Self::Target {
237        self.inner.as_mut().unwrap()
238    }
239}
240
241impl<T: Debug + ?Sized> Debug for MutexGuard<'_, T> {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        Debug::fmt(&self.inner.as_ref().unwrap(), f)
244    }
245}
246
247impl<T: Display + ?Sized> Display for MutexGuard<'_, T> {
248    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
249        (**self).fmt(f)
250    }
251}
252
253impl<T> shuttle_engine::annotations::WithName for &Mutex<T> {
254    fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
255        (&self.semaphore).with_name_and_kind(name, kind.or(Some("shuttle::sync::Mutex")));
256        self
257    }
258}
259
260impl<T> shuttle_engine::annotations::WithName for Mutex<T> {
261    fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
262        (&self).with_name_and_kind(name, kind);
263        self
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270
271    #[test]
272    fn unique_resource_signature_mutex() {
273        shuttle_schedulers::check_random(
274            || {
275                let mutex1 = Mutex::new(0);
276                let mutex2 = Mutex::new(0);
277                assert_ne!(mutex1.semaphore.signature(), mutex2.semaphore.signature());
278            },
279            1,
280        );
281    }
282}