vexide_core/sync/
mutex.rs

1use core::{
2    cell::UnsafeCell,
3    fmt::Debug,
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7use futures_core::Future;
8use lock_api::RawMutex as _;
9
10struct MutexState(AtomicBool);
11impl MutexState {
12    const fn new() -> Self {
13        Self(AtomicBool::new(false))
14    }
15
16    /// Returns true if the lock was acquired.
17    fn try_lock(&self) -> bool {
18        self.0
19            .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
20            .is_ok()
21    }
22
23    fn unlock(&self) {
24        self.0.store(false, Ordering::Release);
25    }
26}
27
28/// A raw, synchronous, spinning mutex type.
29pub(crate) struct RawMutex {
30    state: MutexState,
31}
32impl RawMutex {
33    /// Creates a new raw mutex.
34    #[must_use]
35    #[allow(clippy::new_without_default)]
36    pub const fn new() -> Self {
37        Self {
38            state: MutexState::new(),
39        }
40    }
41}
42unsafe impl lock_api::RawMutex for RawMutex {
43    // Allow this because we can't get around it
44    #[allow(clippy::declare_interior_mutable_const)]
45    const INIT: Self = Self::new();
46
47    type GuardMarker = lock_api::GuardSend;
48
49    fn lock(&self) {
50        while !self.state.try_lock() {
51            core::hint::spin_loop();
52        }
53    }
54
55    fn try_lock(&self) -> bool {
56        self.state.try_lock()
57    }
58
59    unsafe fn unlock(&self) {
60        self.state.unlock();
61    }
62}
63
64/// A future that resolves to a mutex guard.
65#[must_use = "futures do nothing unless you `.await` or poll them"]
66pub struct MutexLockFuture<'a, T: ?Sized> {
67    mutex: &'a Mutex<T>,
68}
69impl<'a, T> Future for MutexLockFuture<'a, T> {
70    type Output = MutexGuard<'a, T>;
71
72    fn poll(
73        self: core::pin::Pin<&mut Self>,
74        _: &mut core::task::Context<'_>,
75    ) -> core::task::Poll<Self::Output> {
76        if self.mutex.raw.try_lock() {
77            core::task::Poll::Ready(MutexGuard::new(self.mutex))
78        } else {
79            core::task::Poll::Pending
80        }
81    }
82}
83
84/// The basic mutex type.
85/// Mutexes are used to share variables between tasks safely.
86pub struct Mutex<T: ?Sized> {
87    raw: RawMutex,
88    data: UnsafeCell<T>,
89}
90unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
91unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
92
93impl<T> Mutex<T> {
94    /// Creates a new mutex.
95    pub const fn new(data: T) -> Self {
96        Self {
97            raw: RawMutex::new(),
98            data: UnsafeCell::new(data),
99        }
100    }
101}
102
103impl<T: ?Sized> Mutex<T> {
104    /// Locks the mutex so that it cannot be locked in another task at the same time.
105    /// Blocks the current task until the lock is acquired.
106    pub const fn lock(&self) -> MutexLockFuture<'_, T> {
107        MutexLockFuture { mutex: self }
108    }
109
110    /// Attempts to acquire this lock. This function does not block.
111    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
112        if self.raw.try_lock() {
113            Some(MutexGuard { mutex: self })
114        } else {
115            None
116        }
117    }
118
119    /// Consumes the mutex and returns the inner data.
120    pub fn into_inner(self) -> T
121    where
122        T: Sized,
123    {
124        self.data.into_inner()
125    }
126
127    /// Gets a mutable reference to the inner data.
128    pub const fn get_mut(&mut self) -> &mut T {
129        self.data.get_mut()
130    }
131}
132
133impl<T> Debug for Mutex<T>
134where
135    T: ?Sized + Debug,
136{
137    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138        struct Placeholder;
139        impl Debug for Placeholder {
140            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141                f.write_str("<locked>")
142            }
143        }
144
145        let mut d = f.debug_struct("Mutex");
146        match self.try_lock() {
147            Some(guard) => d.field("data", &&*guard),
148            None => d.field("data", &Placeholder),
149        };
150        d.finish_non_exhaustive()
151    }
152}
153
154impl<T> Default for Mutex<T>
155where
156    T: Default,
157{
158    fn default() -> Self {
159        Self::new(T::default())
160    }
161}
162
163impl<T> From<T> for Mutex<T> {
164    fn from(value: T) -> Self {
165        Self::new(value)
166    }
167}
168
169/// Allows the user to access the data from a locked mutex.
170/// Dereference to get the inner data.
171#[derive(Debug)]
172#[must_use = "if unused the Mutex will immediately unlock"]
173#[clippy::has_significant_drop]
174pub struct MutexGuard<'a, T: ?Sized> {
175    mutex: &'a Mutex<T>,
176}
177
178impl<'a, T: ?Sized> MutexGuard<'a, T> {
179    const fn new(mutex: &'a Mutex<T>) -> Self {
180        Self { mutex }
181    }
182}
183
184impl<'a, T> MutexGuard<'a, T> {
185    pub(crate) unsafe fn unlock(&self) {
186        // SAFETY: caller must ensure that this is safe
187        unsafe {
188            self.mutex.raw.unlock();
189        }
190    }
191
192    pub(crate) fn relock(self) -> MutexLockFuture<'a, T> {
193        let lock = self.mutex.lock();
194        drop(self);
195        lock
196    }
197}
198
199impl<T: ?Sized> core::ops::Deref for MutexGuard<'_, T> {
200    type Target = T;
201    fn deref(&self) -> &T {
202        unsafe { &*self.mutex.data.get() }
203    }
204}
205
206impl<T: ?Sized> core::ops::DerefMut for MutexGuard<'_, T> {
207    fn deref_mut(&mut self) -> &mut T {
208        unsafe { &mut *self.mutex.data.get() }
209    }
210}
211
212impl<T: ?Sized> Drop for MutexGuard<'_, T> {
213    fn drop(&mut self) {
214        unsafe { self.mutex.raw.unlock() };
215    }
216}