shuttle_std/sync/
mutex.rs1use 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
13pub struct Mutex<T: ?Sized> {
15 state: RefCell<MutexState>,
16 semaphore: BatchSemaphore,
17 inner: std::sync::Mutex<T>,
18}
19
20pub 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 #[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 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 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 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 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 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 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 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 #[inline]
140 pub fn get_mut(&mut self) -> LockResult<&mut T> {
141 self.inner.get_mut()
142 }
143
144 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 self.semaphore.try_acquire(1).unwrap();
154
155 self.inner.into_inner()
156 }
157
158 pub fn clear_poison(&self) {
160 self.inner.clear_poison()
161 }
162}
163
164unsafe impl<T: Send + ?Sized> Send for Mutex<T> {}
169unsafe impl<T: Send + ?Sized> Sync for Mutex<T> {}
170
171impl<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 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 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 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 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}