maitake_sync/blocking/
mutex.rs

1use crate::{
2    blocking::DefaultMutex,
3    loom::cell::{MutPtr, UnsafeCell},
4    util::fmt,
5};
6use core::{
7    marker::PhantomData,
8    ops::{Deref, DerefMut},
9};
10
11pub use mutex_traits::{RawMutex, ScopedRawMutex};
12
13/// A blocking mutual exclusion lock for protecting shared data.
14/// Each mutex has a type parameter which represents
15/// the data that it is protecting. The data can only be accessed through the
16/// RAII guards returned from [`lock`] and [`try_lock`], or within the closures
17/// passed to [`with_lock`] and [`try_with_lock`], which guarantees that
18/// the data is only ever accessed when the mutex is locked.
19///
20/// # Fairness
21///
22/// This is *not* a fair mutex.
23///
24/// # Overriding mutex implementations
25///
26/// This type is generic over a `Lock` type parameter which represents a raw
27/// mutex implementation. By default, this is the [`DefaultMutex`]. To construct
28/// a new `Mutex` with an alternative raw mutex implementation, use the
29/// [`Mutex::new_with_raw_mutex`] cosntructor. See the [module-level documentation
30/// on overriding mutex
31/// implementations](crate::blocking#overriding-mutex-implementations) for
32/// more details.
33///
34/// When `Lock` implements the [`RawMutex`] trait, the [`Mutex`] type provides
35/// the [`lock`] and [`try_lock`] methods, which return a RAII [`MutexGuard`],
36/// similar to the [`std::sync::Mutex`] API, in addition to the scoped
37/// [`with_lock`] and  [`try_with_lock`] methods. When `Lock` only implements
38/// [`ScopedRawMutex`], the [`Mutex`] type provides only the scoped
39/// [`with_lock`] and  [`try_with_lock`] methods.
40///
41/// :warning: Note that [`DefaultMutex`] does *not* implement `RawMutex`, so
42/// using the [`lock`] and [`try_lock`] RAII API requires selecting an
43/// alternative [`RawMutex`] implementation.
44///
45/// # Loom-specific behavior
46///
47/// When `cfg(loom)` is enabled, this mutex will use Loom's simulated atomics,
48/// checked `UnsafeCell`, and simulated spin loop hints.
49///
50/// [`lock`]: Mutex::lock
51/// [`try_lock`]: Mutex::try_lock
52/// [`with_lock`]: Mutex::with_lock
53/// [`try_with_lock`]: Mutex::try_with_lock
54/// [`std::sync::Mutex`]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html
55pub struct Mutex<T, Lock = DefaultMutex> {
56    lock: Lock,
57    data: UnsafeCell<T>,
58}
59
60/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
61/// dropped (falls out of scope), the lock will be unlocked.
62///
63/// The data protected by the mutex can be accessed through this guard via its
64/// [`Deref`] and [`DerefMut`] implementations.
65///
66/// This structure is created by the [`lock`] and [`try_lock`] methods on
67/// [`Mutex`].
68///
69/// [`lock`]: Mutex::lock
70/// [`try_lock`]: Mutex::try_lock
71#[must_use = "if unused, the `Mutex` will immediately unlock"]
72pub struct MutexGuard<'a, T, Lock: RawMutex> {
73    ptr: MutPtr<T>,
74    lock: &'a Lock,
75    _marker: PhantomData<Lock::GuardMarker>,
76}
77
78impl<T> Mutex<T> {
79    loom_const_fn! {
80        /// Returns a new `Mutex` protecting the provided `data`.
81        ///
82        /// The returned `Mutex` is in an unlocked state, ready for use.
83        ///
84        /// This constructor returns a mutex that uses the [`DefaultMutex`]
85        /// implementation. To use an alternative `RawMutex` type, use the
86        /// [`new_with_raw_mutex`](Self::new_with_raw_mutex) constructor, instead.
87        ///
88        /// # Examples
89        ///
90        /// ```
91        /// use maitake_sync::blocking::Mutex;
92        ///
93        /// let mutex = Mutex::new(0);
94        /// ```
95        #[must_use]
96        pub fn new(data: T) -> Self {
97            Self {
98                lock: DefaultMutex::new(),
99                data: UnsafeCell::new(data),
100            }
101        }
102    }
103}
104
105impl<T, Lock> Mutex<T, Lock> {
106    loom_const_fn! {
107        /// Returns a new `Mutex` protecting the provided `data`, using
108        /// `lock` type parameter as the raw mutex implementation.
109        ///
110        /// See the [module-level documentation on overriding mutex
111        /// implementations](crate::blocking#overriding-mutex-implementations) for
112        /// more details.
113        ///
114        /// The returned `Mutex` is in an unlocked state, ready for use.
115        #[must_use]
116        pub fn new_with_raw_mutex(data: T, lock: Lock) -> Self {
117            Self {
118                lock,
119                data: UnsafeCell::new(data),
120            }
121        }
122    }
123
124    /// Consumes this `Mutex`, returning the guarded data.
125    #[inline]
126    #[must_use]
127    pub fn into_inner(self) -> T {
128        self.data.into_inner()
129    }
130
131    /// Returns a mutable reference to the underlying data.
132    ///
133    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
134    /// take place -- the mutable borrow statically guarantees no locks exist.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// let mut lock = maitake_sync::blocking::Mutex::new(0);
140    /// lock.with_lock(|data| *data = 10);
141    /// assert_eq!(*lock.get_mut(), 10);
142    /// ```
143    pub fn get_mut(&mut self) -> &mut T {
144        unsafe {
145            // Safety: since this call borrows the `Mutex` mutably, no actual
146            // locking needs to take place -- the mutable borrow statically
147            // guarantees no locks exist.
148            self.data.with_mut(|data| &mut *data)
149        }
150    }
151}
152
153impl<T, Lock: ScopedRawMutex> Mutex<T, Lock> {
154    /// Lock this `Mutex`, blocking if it is not currently unlocked, and call
155    /// `f()` with the locked data once the lock is acquired.
156    ///
157    /// When the `Mutex` is unlocked, this method locks it, calls `f()` with the
158    /// data protected by the `Mutex`, and then unlocks the `Mutex` and returns
159    /// the result of `f()`. If the `Mutex` is locked, this method blocks until
160    /// it is unlocked, and then takes the lock.
161    ///
162    /// To return immediately rather than blocking, use [`Mutex::try_with_lock`]
163    /// instead.
164    ///
165    /// This method is available as long as the `Mutex`'s `Lock` type parameter
166    /// implements the [`ScopedRawMutex`] trait. See the [module-level
167    /// documentation on overriding mutex
168    /// implementations](crate::blocking#overriding-mutex-implementations) for
169    /// more details.
170    #[track_caller]
171    pub fn with_lock<U>(&self, f: impl FnOnce(&mut T) -> U) -> U {
172        self.lock.with_lock(|| {
173            self.data.with_mut(|data| unsafe {
174                // Safety: we just locked the mutex.
175                f(&mut *data)
176            })
177        })
178    }
179
180    /// Attempt to lock this `Mutex` without blocking and call `f()` with the
181    /// locked data if the lock is acquired.
182    ///
183    /// If the `Mutex` is unlocked, this method locks it, calls `f()` with the
184    /// data protected by the `Mutex`, and then unlocks the `Mutex` and returns
185    /// [`Some`]`(U)`. Otherwise, if the lock is already held, this method
186    /// returns `None` immediately, without blocking.
187    ///
188    /// To block until the `Mutex` is unlocked instead of returning `None`, use
189    /// [`Mutex::with_lock`] instead.
190    ///
191    /// This method is available as long as the `Mutex`'s `Lock` type parameter
192    /// implements the [`ScopedRawMutex`] trait. See the [module-level
193    /// documentation on overriding mutex
194    /// implementations](crate::blocking#overriding-mutex-implementations) for
195    /// more details.
196    ///
197    /// # Returns
198    ///
199    /// - [`Some`]`(U)` if the lock was acquired, containing the result of
200    ///   `f()`.
201    /// - [`None`] if the lock is currently held and could not be acquired
202    ///   without blocking.
203    #[track_caller]
204    pub fn try_with_lock<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
205        self.lock.try_with_lock(|| {
206            self.data.with_mut(|data| unsafe {
207                // Safety: we just locked the mutex.
208                f(&mut *data)
209            })
210        })
211    }
212}
213
214impl<T, Lock> Mutex<T, Lock>
215where
216    Lock: RawMutex,
217{
218    fn guard(&self) -> MutexGuard<'_, T, Lock> {
219        MutexGuard {
220            ptr: self.data.get_mut(),
221            lock: &self.lock,
222            _marker: PhantomData,
223        }
224    }
225
226    /// Attempts to acquire this lock without blocking
227    ///
228    /// If the lock could not be acquired at this time, then [`None`] is returned.
229    /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
230    /// guard is dropped.
231    ///
232    /// This function will never block.
233    ///
234    /// This method is only availble if the `Mutex`'s `Lock` type parameter
235    /// implements the [`RawMutex`] trait. See the [module-level documentation
236    /// on overriding mutex
237    /// implementations](crate::blocking#overriding-mutex-implementations) for
238    /// more details.
239    #[must_use]
240    #[cfg_attr(test, track_caller)]
241    pub fn try_lock(&self) -> Option<MutexGuard<'_, T, Lock>> {
242        if self.lock.try_lock() {
243            Some(self.guard())
244        } else {
245            None
246        }
247    }
248
249    /// Acquires a mutex, blocking until it is locked.
250    ///
251    /// This function will block until the mutex is available to lock. Upon
252    /// returning, the thread is the only thread with the lock
253    /// held. An RAII guard is returned to allow scoped unlock of the lock. When
254    /// the guard goes out of scope, the mutex will be unlocked.
255    ///
256    /// This method is only availble if the `Mutex`'s `Lock` type parameter
257    /// implements the [`RawMutex`] trait. See the [module-level documentation
258    /// on overriding mutex
259    /// implementations](crate::blocking#overriding-mutex-implementations) for
260    /// more details.
261    #[cfg_attr(test, track_caller)]
262    pub fn lock(&self) -> MutexGuard<'_, T, Lock> {
263        self.lock.lock();
264        self.guard()
265    }
266
267    /// Forcibly unlock the mutex.
268    ///
269    /// If a lock is currently held, it will be released, regardless of who's
270    /// holding it. Of course, this is **outrageously, disgustingly unsafe** and
271    /// you should never do it.
272    ///
273    /// This method is only availble if the `Mutex`'s `Lock` type parameter
274    /// implements the [`RawMutex`] trait. See the [module-level documentation
275    /// on overriding mutex
276    /// implementations](crate::blocking#overriding-mutex-implementations) for
277    /// more details.
278    ///
279    /// # Safety
280    ///
281    /// This deliberately violates mutual exclusion.
282    ///
283    /// Only call this method when it is _guaranteed_ that no stack frame that
284    /// has previously locked the mutex will ever continue executing.
285    /// Essentially, this is only okay to call when the kernel is oopsing and
286    /// all code running on other cores has already been killed.
287    pub unsafe fn force_unlock(&self) {
288        self.lock.unlock()
289    }
290}
291
292impl<T: Default, Lock: Default> Default for Mutex<T, Lock> {
293    fn default() -> Self {
294        Self {
295            lock: Default::default(),
296            data: UnsafeCell::new(Default::default()),
297        }
298    }
299}
300
301impl<T, Lock> fmt::Debug for Mutex<T, Lock>
302where
303    T: fmt::Debug,
304    Lock: ScopedRawMutex,
305{
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        self.try_with_lock(|data| {
308            f.debug_struct("Mutex")
309                .field("data", data)
310                .field("lock", &format_args!("{}", core::any::type_name::<Lock>()))
311                .finish()
312        })
313        .unwrap_or_else(|| {
314            f.debug_struct("Mutex")
315                .field("data", &format_args!("<locked>"))
316                .field("lock", &format_args!("{}", core::any::type_name::<Lock>()))
317                .finish()
318        })
319    }
320}
321
322unsafe impl<T: Send, Lock: Send> Send for Mutex<T, Lock> {}
323/// A `Mutex` is [`Sync`] if `T` is [`Send`] and `Lock` is [`Sync`].
324///
325/// `T` must be [`Send`] because shared references to the `Mutex` allow mutable
326/// access to `T` (via a [`MutexGuard`] or [`Mutex::with_lock`]), which can be
327/// used to move `T` between threads using [`core::mem::replace`] or similar.
328/// `T` does **not** need to be [`Sync`], and, in fact, a `Mutex` is often used
329/// to protect `!Sync` data.
330///
331/// The `Lock` type must be `Sync` because sharing references to a mutex
332/// implicitly share references to the `Lock` type as well --- locking the mutex
333/// references it.
334unsafe impl<T: Send, Lock: Sync> Sync for Mutex<T, Lock> {}
335
336// === impl MutexGuard ===
337
338impl<T, Lock: RawMutex> Deref for MutexGuard<'_, T, Lock> {
339    type Target = T;
340    #[inline]
341    fn deref(&self) -> &Self::Target {
342        unsafe {
343            // Safety: we are holding the lock, so it is okay to dereference the
344            // mut pointer.
345            &*self.ptr.deref()
346        }
347    }
348}
349
350impl<T, Lock: RawMutex> DerefMut for MutexGuard<'_, T, Lock> {
351    #[inline]
352    fn deref_mut(&mut self) -> &mut Self::Target {
353        unsafe {
354            // Safety: we are holding the lock, so it is okay to dereference the
355            // mut pointer.
356            self.ptr.deref()
357        }
358    }
359}
360
361impl<T, Lock, R: ?Sized> AsRef<R> for MutexGuard<'_, T, Lock>
362where
363    T: AsRef<R>,
364    Lock: RawMutex,
365{
366    #[inline]
367    fn as_ref(&self) -> &R {
368        self.deref().as_ref()
369    }
370}
371
372impl<T, Lock, R: ?Sized> AsMut<R> for MutexGuard<'_, T, Lock>
373where
374    T: AsMut<R>,
375    Lock: RawMutex,
376{
377    #[inline]
378    fn as_mut(&mut self) -> &mut R {
379        self.deref_mut().as_mut()
380    }
381}
382
383impl<T, Lock> Drop for MutexGuard<'_, T, Lock>
384where
385    Lock: RawMutex,
386{
387    #[inline]
388    #[cfg_attr(test, track_caller)]
389    fn drop(&mut self) {
390        unsafe { self.lock.unlock() }
391    }
392}
393
394impl<T, Lock> fmt::Debug for MutexGuard<'_, T, Lock>
395where
396    T: fmt::Debug,
397    Lock: RawMutex,
398{
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        self.deref().fmt(f)
401    }
402}
403
404impl<T, Lock> fmt::Display for MutexGuard<'_, T, Lock>
405where
406    T: fmt::Display,
407    Lock: RawMutex,
408{
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        self.deref().fmt(f)
411    }
412}
413
414/// A [`MutexGuard`] is only [`Send`] if:
415///
416/// 1. the protected data (`T`) is `Send`, because the guard may be used to
417///    mutably access the protected data, and can therefore be used to move it
418///    using [`core::mem::replace`] or similar.
419/// 2. the `Lock` type parameter is [`Sync`], because the guard contains a
420///    reference to the `Lock` type, and therefore, sending the guard is sharing
421///    a reference to the `Lock`.
422/// 3. the `Lock` type's [`RawMutex::GuardMarker`] associated type is [`Send`],
423///    because this indicates that the `Lock` type agrees that guards may be
424///    [`Send`].
425unsafe impl<T, Lock> Send for MutexGuard<'_, T, Lock>
426where
427    T: Send,
428    Lock: RawMutex + Sync,
429    Lock::GuardMarker: Send,
430{
431}
432
433#[cfg(test)]
434mod tests {
435    use crate::loom::{self, thread};
436    use crate::spin::Spinlock;
437    use std::prelude::v1::*;
438    use std::sync::Arc;
439
440    use super::*;
441
442    #[test]
443    fn multithreaded() {
444        loom::model(|| {
445            let mutex = Arc::new(Mutex::new_with_raw_mutex(String::new(), Spinlock::new()));
446            let mutex2 = mutex.clone();
447
448            let t1 = thread::spawn(move || {
449                tracing::info!("t1: locking...");
450                let mut lock = mutex2.lock();
451                tracing::info!("t1: locked");
452                lock.push_str("bbbbb");
453                tracing::info!("t1: dropping...");
454            });
455
456            {
457                tracing::info!("t2: locking...");
458                let mut lock = mutex.lock();
459                tracing::info!("t2: locked");
460                lock.push_str("bbbbb");
461                tracing::info!("t2: dropping...");
462            }
463            t1.join().unwrap();
464        });
465    }
466
467    #[test]
468    fn try_lock() {
469        loom::model(|| {
470            let mutex = Mutex::new_with_raw_mutex(42, Spinlock::new());
471            // First lock succeeds
472            let a = mutex.try_lock();
473            assert_eq!(a.as_ref().map(|r| **r), Some(42));
474
475            // Additional lock failes
476            let b = mutex.try_lock();
477            assert!(b.is_none());
478
479            // After dropping lock, it succeeds again
480            ::core::mem::drop(a);
481            let c = mutex.try_lock();
482            assert_eq!(c.as_ref().map(|r| **r), Some(42));
483        });
484    }
485}