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 shuttle_engine::runtime::thread;
8use std::cell::RefCell;
9use std::fmt::{Debug, Display};
10use std::ops::{Deref, DerefMut};
11use std::panic::{RefUnwindSafe, UnwindSafe};
12use tracing::trace;
13
14pub struct Mutex<T: ?Sized> {
16 state: RefCell<MutexState>,
17 semaphore: BatchSemaphore,
18 inner: std::sync::Mutex<T>,
19}
20
21pub struct MutexGuard<'a, T: ?Sized> {
23 inner: Option<std::sync::MutexGuard<'a, T>>,
24 mutex: &'a Mutex<T>,
25}
26
27#[derive(Debug)]
28struct MutexState {
29 holder: Option<TaskId>,
30}
31
32impl<T> Mutex<T> {
33 #[track_caller]
35 pub const fn new(value: T) -> Self {
36 Self::new_internal(value, ResourceSignature::new_const(ResourceType::Mutex))
37 }
38
39 pub(crate) const fn new_internal(value: T, signature: ResourceSignature) -> Self {
40 let state = MutexState { holder: None };
41 Self {
42 state: RefCell::new(state),
43 semaphore: BatchSemaphore::const_new_with_signature(1, Fairness::Unfair, signature),
44 inner: std::sync::Mutex::new(value),
45 }
46 }
47}
48
49impl<T: ?Sized> Mutex<T> {
50 pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
52 let me = current::me();
53
54 let mut state = self.state.borrow_mut();
55 trace!(holder=?state.holder, semaphore=?self.semaphore, "waiting to acquire mutex {:p}", self);
56 drop(state);
57
58 if !self.semaphore.is_closed() {
59 state = self.state.borrow_mut();
61 assert!(
62 match &state.holder {
63 Some(holder) => *holder != me,
64 None => true,
65 },
66 "deadlock! task {me:?} tried to acquire a Mutex it already holds"
67 );
68 drop(state);
69
70 self.semaphore.acquire_blocking(1).unwrap();
71 } else {
72 thread::switch();
74 }
75
76 state = self.state.borrow_mut();
77 assert!(state.holder.is_none());
78 state.holder = Some(me);
79 drop(state);
80
81 trace!(semaphore=?self.semaphore, "acquired mutex {:p}", self);
82
83 let result = match self.inner.try_lock() {
85 Ok(guard) => Ok(MutexGuard {
86 inner: Some(guard),
87 mutex: self,
88 }),
89 Err(TryLockError::Poisoned(guard)) => Err(PoisonError::new(MutexGuard {
90 inner: Some(guard.into_inner()),
91 mutex: self,
92 })),
93 Err(TryLockError::WouldBlock) => unreachable!("mutex state out of sync"),
94 };
95
96 result
97 }
98
99 pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
104 let me = current::me();
105
106 let mut state = self.state.borrow_mut();
107 trace!(holder=?state.holder, semaphore=?self.semaphore, "trying to acquire mutex {:p}", self);
108 drop(state);
109
110 self.semaphore.try_acquire(1).map_err(|_| TryLockError::WouldBlock)?;
114
115 state = self.state.borrow_mut();
116 state.holder = Some(me);
117 drop(state);
118
119 trace!(semaphore=?self.semaphore, "acquired mutex {:p}", self);
120
121 let result = match self.inner.try_lock() {
123 Ok(guard) => Ok(MutexGuard {
124 inner: Some(guard),
125 mutex: self,
126 }),
127 Err(TryLockError::Poisoned(guard)) => Err(TryLockError::Poisoned(PoisonError::new(MutexGuard {
128 inner: Some(guard.into_inner()),
129 mutex: self,
130 }))),
131 Err(TryLockError::WouldBlock) => unreachable!("mutex state out of sync"),
132 };
133
134 result
135 }
136
137 #[inline]
142 pub fn get_mut(&mut self) -> LockResult<&mut T> {
143 self.inner.get_mut()
144 }
145
146 pub fn into_inner(self) -> LockResult<T>
148 where
149 T: Sized,
150 {
151 let state = self.state.borrow();
152 assert!(state.holder.is_none());
153
154 self.semaphore.try_acquire(1).unwrap();
156
157 self.inner.into_inner()
158 }
159
160 pub fn clear_poison(&self) {
162 self.inner.clear_poison()
163 }
164}
165
166unsafe impl<T: Send + ?Sized> Send for Mutex<T> {}
171unsafe impl<T: Send + ?Sized> Sync for Mutex<T> {}
172
173impl<T: ?Sized> UnwindSafe for Mutex<T> {}
175impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
176
177impl<T: Default> Default for Mutex<T> {
178 fn default() -> Self {
179 Self::new(Default::default())
180 }
181}
182
183impl<T: ?Sized + Debug> Debug for Mutex<T> {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 Debug::fmt(&self.inner, f)
186 }
187}
188
189impl<'a, T: ?Sized> MutexGuard<'a, T> {
190 pub(super) fn unlock(self) -> &'a Mutex<T> {
192 self.mutex
193 }
194}
195
196impl<T: ?Sized> Drop for MutexGuard<'_, T> {
197 fn drop(&mut self) {
198 struct Unlock<'g, 'a, T: ?Sized>(&'g mut MutexGuard<'a, T>);
203 impl<T: ?Sized> Drop for Unlock<'_, '_, T> {
204 #[inline]
205 fn drop(&mut self) {
206 let guard = &mut *self.0;
207 let clear_poison = std::thread::panicking()
210 && !guard.mutex.inner.is_poisoned()
211 && ExecutionState::unwinding_for_teardown();
212 guard.inner = None;
213 if clear_poison {
214 guard.mutex.inner.clear_poison();
215 }
216
217 let mut state = guard.mutex.state.borrow_mut();
218 trace!(semaphore=?guard.mutex.semaphore, "releasing mutex {:p}", guard.mutex);
219 state.holder = None;
220 }
221 }
222 let unlock = Unlock(self);
223
224 unlock.0.mutex.semaphore.release(1);
226 }
227}
228
229impl<T: ?Sized> Deref for MutexGuard<'_, T> {
230 type Target = T;
231
232 fn deref(&self) -> &Self::Target {
233 self.inner.as_ref().unwrap()
234 }
235}
236
237impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
238 fn deref_mut(&mut self) -> &mut Self::Target {
239 self.inner.as_mut().unwrap()
240 }
241}
242
243impl<T: Debug + ?Sized> Debug for MutexGuard<'_, T> {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 Debug::fmt(&self.inner.as_ref().unwrap(), f)
246 }
247}
248
249impl<T: Display + ?Sized> Display for MutexGuard<'_, T> {
250 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
251 (**self).fmt(f)
252 }
253}
254
255impl<T> shuttle_engine::annotations::WithName for &Mutex<T> {
256 fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
257 (&self.semaphore).with_name_and_kind(name, kind.or(Some("shuttle::sync::Mutex")));
258 self
259 }
260}
261
262impl<T> shuttle_engine::annotations::WithName for Mutex<T> {
263 fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
264 (&self).with_name_and_kind(name, kind);
265 self
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn unique_resource_signature_mutex() {
275 shuttle_schedulers::check_random(
276 || {
277 let mutex1 = Mutex::new(0);
278 let mutex2 = Mutex::new(0);
279 assert_ne!(mutex1.semaphore.signature(), mutex2.semaphore.signature());
280 },
281 1,
282 );
283 }
284}