1use crate::core::detector;
2use crate::core::locks::{
3 NEXT_LOCK_ID,
4 contention::{ContentionState, SlowWaiter},
5};
6
7use crate::core::types::{LockId, ThreadId, get_current_thread_id};
8#[cfg(feature = "logging-and-visualization")]
9use crate::core::{Events, logger};
10use parking_lot::{Mutex as ParkingLotMutex, MutexGuard as ParkingLotMutexGuard};
11use std::ops::{Deref, DerefMut};
12use std::sync::atomic::{AtomicUsize, Ordering};
13
14pub struct Mutex<T> {
44 id: LockId,
46 inner: ParkingLotMutex<T>,
48 creator_thread_id: ThreadId,
50 state: MutexState,
52}
53
54struct MutexState {
55 owner: AtomicUsize,
57 contention: ContentionState,
59}
60
61pub struct MutexGuard<'a, T> {
67 thread_id: ThreadId,
69 lock_id: LockId,
71 guard: ParkingLotMutexGuard<'a, T>,
73 state: &'a MutexState,
75 tracked_globally: bool,
77}
78
79impl<T> Mutex<T> {
80 pub fn new(value: T) -> Self {
96 let id = NEXT_LOCK_ID.fetch_add(1, Ordering::SeqCst);
97 let creator_thread_id = get_current_thread_id();
98
99 detector::mutex::create_mutex(id, Some(creator_thread_id));
101
102 Mutex {
103 id,
104 inner: ParkingLotMutex::new(value),
105 creator_thread_id,
106 state: MutexState {
107 owner: AtomicUsize::new(0),
108 contention: ContentionState::new(),
109 },
110 }
111 }
112
113 pub fn id(&self) -> LockId {
118 self.id
119 }
120
121 pub fn creator_thread_id(&self) -> ThreadId {
126 self.creator_thread_id
127 }
128
129 pub fn lock(&self) -> MutexGuard<'_, T> {
148 let thread_id = get_current_thread_id();
149 let tid_usize = thread_id;
150
151 #[cfg(not(feature = "stress-test"))]
153 if let Some(guard) = self.inner.try_lock() {
154 self.state.owner.store(tid_usize, Ordering::Release);
155 let tracked_globally =
156 cfg!(feature = "lock-order-graph") || self.state.contention.has_waiters();
157
158 #[cfg(feature = "logging-and-visualization")]
159 {
160 if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
161 logger::log_interaction_event(thread_id, self.id, Events::MutexAttempt);
162 }
163 }
164
165 if tracked_globally {
166 detector::mutex::complete_acquire(thread_id, self.id);
167 }
168
169 #[cfg(feature = "logging-and-visualization")]
170 {
171 if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
172 logger::log_interaction_event(thread_id, self.id, Events::MutexAcquired);
173 }
174 }
175
176 return MutexGuard {
177 thread_id,
178 lock_id: self.id,
179 guard,
180 state: &self.state,
181 tracked_globally,
182 };
183 }
184
185 let slow_waiter = self.state.contention.register();
187 let (rechecked_guard, deadlock_info) = detector::mutex::acquire_slow_with_recheck(
188 thread_id,
189 self.id,
190 || self.inner.try_lock(),
191 || {
192 let owner = self.state.owner.load(Ordering::Acquire);
193 (owner != 0).then_some(owner as ThreadId)
194 },
195 );
196
197 if let Some(info) = deadlock_info {
198 detector::deadlock_handling::process_deadlock(info);
199 }
200
201 if let Some(guard) = rechecked_guard {
202 self.state.owner.store(tid_usize, Ordering::Release);
203 drop(slow_waiter);
204 return MutexGuard {
205 thread_id,
206 lock_id: self.id,
207 guard,
208 state: &self.state,
209 tracked_globally: true,
210 };
211 }
212
213 let guard = self.inner.lock();
214 self.state.owner.store(tid_usize, Ordering::Release);
215 detector::mutex::complete_acquire(thread_id, self.id);
216 drop(slow_waiter);
217
218 MutexGuard {
219 thread_id,
220 lock_id: self.id,
221 guard,
222 state: &self.state,
223 tracked_globally: true,
224 }
225 }
226
227 pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
248 let thread_id = get_current_thread_id();
249 let tid_usize = thread_id;
250
251 if let Some(guard) = self.inner.try_lock() {
252 self.state.owner.store(tid_usize, Ordering::Release);
253 let tracked_globally =
254 cfg!(feature = "lock-order-graph") || self.state.contention.has_waiters();
255
256 #[cfg(feature = "logging-and-visualization")]
257 {
258 if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
259 logger::log_interaction_event(thread_id, self.id, Events::MutexAttempt);
260 }
261 }
262
263 if tracked_globally {
264 detector::mutex::complete_acquire(thread_id, self.id);
265 }
266
267 #[cfg(feature = "logging-and-visualization")]
268 {
269 if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
270 logger::log_interaction_event(thread_id, self.id, Events::MutexAcquired);
271 }
272 }
273
274 Some(MutexGuard {
275 thread_id,
276 lock_id: self.id,
277 guard,
278 state: &self.state,
279 tracked_globally,
280 })
281 } else {
282 None
283 }
284 }
285
286 pub fn into_inner(self) -> T
298 where
299 T: Sized,
300 {
301 detector::mutex::destroy_mutex(self.id);
304
305 let mutex = std::mem::ManuallyDrop::new(self);
307
308 unsafe { std::ptr::read(&mutex.inner) }.into_inner()
310 }
311
312 pub fn get_mut(&mut self) -> &mut T {
327 self.inner.get_mut()
328 }
329}
330
331impl<T> Drop for Mutex<T> {
332 fn drop(&mut self) {
333 detector::mutex::destroy_mutex(self.id);
335 }
336}
337
338impl<T> Deref for MutexGuard<'_, T> {
339 type Target = T;
340
341 fn deref(&self) -> &Self::Target {
342 self.guard.deref()
343 }
344}
345
346impl<T> DerefMut for MutexGuard<'_, T> {
347 fn deref_mut(&mut self) -> &mut Self::Target {
348 self.guard.deref_mut()
349 }
350}
351
352impl<'a, T> MutexGuard<'a, T> {
353 pub(crate) fn inner_guard(&mut self) -> &mut ParkingLotMutexGuard<'a, T> {
358 &mut self.guard
359 }
360
361 pub(crate) fn lock_id(&self) -> LockId {
365 self.lock_id
366 }
367
368 pub(crate) fn register_condvar_waiter(&self) -> SlowWaiter<'a> {
370 self.state.contention.register()
371 }
372
373 pub(crate) fn clear_ownership(&self) {
375 self.state.owner.store(0, Ordering::Release);
376 }
377
378 pub(crate) fn restore_ownership(&self) {
380 self.state.owner.store(self.thread_id, Ordering::Release);
381 }
382
383 pub(crate) fn mark_tracked_globally(&mut self) {
384 self.tracked_globally = true;
385 }
386
387 #[cfg(all(test, not(feature = "lock-order-graph")))]
388 pub(crate) fn is_tracked_globally(&self) -> bool {
389 self.tracked_globally
390 }
391}
392
393impl<T> Drop for MutexGuard<'_, T> {
394 fn drop(&mut self) {
395 self.state.owner.store(0, Ordering::Release);
397
398 if self.tracked_globally || self.state.contention.has_waiters() {
400 detector::mutex::release_mutex(self.thread_id, self.lock_id);
401 } else {
402 #[cfg(feature = "logging-and-visualization")]
403 if logger::LOGGING_ENABLED.load(Ordering::Relaxed) {
404 logger::log_interaction_event(self.thread_id, self.lock_id, Events::MutexReleased);
405 }
406 }
407 }
408}
409
410impl<T: Default> Default for Mutex<T> {
413 fn default() -> Mutex<T> {
415 Mutex::new(Default::default())
416 }
417}
418
419impl<T> From<T> for Mutex<T> {
420 fn from(t: T) -> Self {
423 Mutex::new(t)
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use std::mem::size_of;
431 use std::sync::{Arc, mpsc};
432 use std::time::{Duration, Instant};
433
434 #[test]
435 fn mutex_guard_keeps_one_tracking_reference() {
436 let maximum_size = size_of::<ParkingLotMutexGuard<'static, ()>>() + 4 * size_of::<usize>();
437
438 assert!(
439 size_of::<MutexGuard<'static, ()>>() <= maximum_size,
440 "guard stores more than one tracking reference"
441 );
442 }
443
444 #[test]
445 fn blocking_mutex_wait_is_visible_until_acquisition() {
446 let lock = Arc::new(Mutex::new(()));
447 let owner = lock.lock();
448 let waiter_lock = Arc::clone(&lock);
449 let (acquired_tx, acquired_rx) = mpsc::channel();
450
451 let waiter = std::thread::spawn(move || {
452 let _guard = waiter_lock.lock();
453 acquired_tx.send(()).unwrap();
454 });
455
456 let deadline = Instant::now() + Duration::from_secs(1);
457 while !lock.state.contention.has_waiters() && Instant::now() < deadline {
458 std::thread::yield_now();
459 }
460 assert!(lock.state.contention.has_waiters());
461
462 drop(owner);
463 acquired_rx.recv_timeout(Duration::from_secs(1)).unwrap();
464 waiter.join().unwrap();
465 assert!(!lock.state.contention.has_waiters());
466 }
467}