Skip to main content

dtact_util/sync/
mutex.rs

1//! Async mutex — the primitive most people mean by "nonblocking mutex".
2
3use super::wait_queue::WaitQueue;
4use std::cell::UnsafeCell;
5use std::ops::{Deref, DerefMut};
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::task::{Context, Poll};
8
9/// An async mutex: `.lock().await` yields the calling task (not the OS
10/// thread) while the lock is held elsewhere, instead of blocking it.
11///
12/// # Errors
13///
14/// None of this type's methods can fail (no lock poisoning — a panic
15/// while holding the guard simply unlocks on unwind, matching
16/// `tokio::sync::Mutex`'s behavior, not `std::sync::Mutex`'s).
17#[repr(align(64))]
18pub struct Mutex<T: ?Sized> {
19    locked: AtomicBool,
20    wait: WaitQueue,
21    data: UnsafeCell<T>,
22}
23
24// SAFETY: `T: Send` is required to send the guarded value across threads
25// (a guard obtained on one thread can be dropped, or the mutex moved, on
26// another); the `locked` CAS is the sole gate on `data` access, so `T:
27// Sync` isn't needed for `Mutex<T>: Sync` the way it would be for a type
28// exposing `&T` without exclusion.
29unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
30unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
31
32impl<T> Mutex<T> {
33    /// Create a new mutex guarding `data`, initially unlocked.
34    #[must_use]
35    pub const fn new(data: T) -> Self {
36        Self {
37            locked: AtomicBool::new(false),
38            wait: WaitQueue::new(),
39            data: UnsafeCell::new(data),
40        }
41    }
42
43    /// Consume the mutex, returning the guarded value.
44    #[inline(always)]
45    pub fn into_inner(self) -> T {
46        self.data.into_inner()
47    }
48}
49
50impl<T: ?Sized> Mutex<T> {
51    /// Acquire the lock, waiting (without blocking the OS thread) if it's
52    /// currently held elsewhere.
53    #[inline(always)]
54    pub async fn lock(&self) -> MutexGuard<'_, T> {
55        std::future::poll_fn(|cx| self.poll_lock(cx)).await
56    }
57
58    /// Acquire the lock if it's immediately available, without waiting.
59    #[must_use]
60    #[inline(always)]
61    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
62        self.locked
63            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
64            .is_ok()
65            // `then_some` evaluates its argument eagerly — with a guard
66            // type whose `Drop` releases the lock, that would construct
67            // (and, on the `false` branch, immediately drop-and-release)
68            // a guard even when the CAS failed, unlocking a mutex someone
69            // else legitimately holds. `then` with a closure defers
70            // construction until we know the CAS actually succeeded.
71            .then(|| MutexGuard { mutex: self })
72    }
73
74    #[inline]
75    fn poll_lock(&self, cx: &Context<'_>) -> Poll<MutexGuard<'_, T>> {
76        // Skip the immediate fast-path CAS if anyone is already
77        // registered waiting for the lock — otherwise a fresh `.lock()`
78        // call can win the CAS against an already-waiting task every
79        // single time the lock is released, starving it indefinitely
80        // under sustained contention. See `WaitQueue::has_waiters`'s doc
81        // (written up against the identical, confirmed-reproducible bug
82        // in `mpsc`'s bounded channel) for the full explanation. `try_lock`
83        // itself is unaffected — it's an explicit "don't wait" API and
84        // must always attempt the CAS regardless of waiters.
85        if !self.wait.has_waiters()
86            && let Some(guard) = self.try_lock()
87        {
88            return Poll::Ready(guard);
89        }
90        // Register before the re-check below (not after) so a release
91        // that happens between our failed CAS above and this register
92        // can't be missed: `wake_one` always looks at the queue *after*
93        // clearing `locked`, so if we're not in the queue yet when it
94        // runs, its wakeup is lost — registering first, then re-checking,
95        // closes that window the same way every other primitive in this
96        // module does.
97        let token = self.wait.register(cx.waker());
98        if let Some(guard) = self.try_lock() {
99            // Our own CAS succeeded without needing the wake — retract
100            // the registration above so it doesn't sit around as dead
101            // weight for `wake_one` to work around later. See
102            // `WaitQueue::cancel`'s doc for why this matters beyond tidiness.
103            self.wait.cancel(token);
104            return Poll::Ready(guard);
105        }
106        Poll::Pending
107    }
108
109    /// Get mutable access to the guarded value without locking — sound
110    /// because `&mut self` statically proves no other reference (locked
111    /// or not) can exist.
112    #[inline(always)]
113    pub const fn get_mut(&mut self) -> &mut T {
114        self.data.get_mut()
115    }
116}
117
118impl<T: Default> Default for Mutex<T> {
119    fn default() -> Self {
120        Self {
121            locked: AtomicBool::new(false),
122            wait: WaitQueue::new(),
123            data: UnsafeCell::new(T::default()),
124        }
125    }
126}
127
128/// RAII guard for a locked [`Mutex`]. Releases the lock (and wakes one
129/// waiter, if any) on drop.
130#[repr(align(64))]
131pub struct MutexGuard<'a, T: ?Sized> {
132    mutex: &'a Mutex<T>,
133}
134
135// SAFETY: a `MutexGuard` is proof the lock is held, so `&T`/`&mut T`
136// through it never overlaps another guard's access. Sending the guard
137// itself across threads (unlocking on whichever thread drops it) is sound
138// as long as `T: Send`, same as `std`/`tokio`'s own guards.
139unsafe impl<T: ?Sized + Send> Send for MutexGuard<'_, T> {}
140unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
141
142impl<T: ?Sized> Deref for MutexGuard<'_, T> {
143    type Target = T;
144    #[inline(always)]
145    fn deref(&self) -> &T {
146        // SAFETY: holding a `MutexGuard` is exclusive proof of the lock.
147        unsafe { &*self.mutex.data.get() }
148    }
149}
150
151impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
152    #[inline(always)]
153    fn deref_mut(&mut self) -> &mut T {
154        // SAFETY: same as `Deref` above.
155        unsafe { &mut *self.mutex.data.get() }
156    }
157}
158
159impl<T: ?Sized> Drop for MutexGuard<'_, T> {
160    #[inline(always)]
161    fn drop(&mut self) {
162        self.mutex.locked.store(false, Ordering::Release);
163        self.mutex.wait.wake_one();
164    }
165}