maitake_sync/
mutex.rs

1//! An asynchronous [mutual exclusion lock].
2//!
3//! See the documentation on the [`Mutex`] type for details.
4//!
5//! [mutual exclusion lock]: https://en.wikipedia.org/wiki/Mutual_exclusion
6use crate::{
7    blocking::{DefaultMutex, ScopedRawMutex},
8    loom::cell::{MutPtr, UnsafeCell},
9    util::fmt,
10    wait_queue::{self, WaitQueue},
11};
12use core::{
13    future::Future,
14    ops::{Deref, DerefMut},
15    pin::Pin,
16    task::{Context, Poll},
17};
18use pin_project::pin_project;
19#[cfg(test)]
20mod tests;
21
22/// An asynchronous [mutual exclusion lock][mutex] for protecting shared data.
23///
24/// The data can only be accessed through the [RAII guards] returned
25/// from [`lock`] and [`try_lock`], which guarantees that the data is only ever
26/// accessed when the mutex is locked.
27///
28/// # Comparison With Other Mutices
29///
30/// This is an *asynchronous* mutex. When the shared data is locked, the
31/// [`lock`] method will wait by causing the current [task] to yield until the
32/// shared data is available. This is in contrast to *blocking* mutices, such as
33/// [`std::sync::Mutex`], which wait by blocking the current thread[^1], or
34/// *spinlock* based mutices, such as [`blocking::Mutex`], which wait by spinning
35/// in a busy loop.
36///
37/// The [`futures-util`] crate also provides an implementation of an asynchronous
38/// mutex, [`futures_util::lock::Mutex`]. However, this mutex requires the Rust
39/// standard library, and is thus unsuitable for use in environments where the
40/// standard library is unavailable. In addition, the `futures-util` mutex
41/// requires an additional allocation for every task that is waiting to acquire
42/// the lock, while `maitake`'s mutex is based on an [intrusive linked list],
43/// and therefore can be used without allocation[^2]. This makes `maitake`'s
44/// mutex suitable for environments where heap allocations must be minimized or
45/// cannot be used at all.
46///
47/// In addition, this is a [fairly queued] mutex. This means that the lock is
48/// always acquired in a first-in, first-out order — if a task acquires
49/// and then releases the lock, and then wishes to acquire the lock again, it
50/// will not acquire the lock until every other task ahead of it in the queue
51/// has had a chance to lock the shared data. Again, this is in contrast to
52/// [`std::sync::Mutex`], where fairness depends on the underlying OS' locking
53/// primitives; and [`blocking::Mutex`] and [`futures_util::lock::Mutex`], which
54/// will never guarantee fairness.
55///
56/// Finally, this mutex does not implement [poisoning][^3], unlike
57/// [`std::sync::Mutex`].
58///
59/// # Overriding the blocking mutex
60///
61/// This type uses a [blocking `Mutex`](crate::blocking::Mutex) internally to
62/// synchronize access to its wait list. By default, the [`DefaultMutex`] type
63/// is used as the underlying mutex implementation. To use an alternative
64/// [`ScopedRawMutex`] implementation, use the
65/// [`new_with_raw_mutex`](Self::new_with_raw_mutex) constructor. See [the documentation
66/// on overriding mutex
67/// implementations](crate::blocking#overriding-mutex-implementations) for more
68/// details.
69///
70/// [^1]: And therefore require an operating system to manage threading.
71///
72/// [^2]: The [tasks](core::task) themselves must, of course, be stored
73///     somewhere, but this need not be a heap allocation in systems with a
74///     fixed set of statically-allocated tasks. And, when tasks *are*
75///     heap-allocated, these allocations [need not be provided by
76///     `liballoc`][storage].
77///
78/// [^3]: In fact, this mutex _cannot_ implement poisoning, as poisoning
79///     requires support for unwinding, and [`maitake` assumes that panics are
80///     invariably fatal][no-unwinding].
81///
82/// [mutex]: https://en.wikipedia.org/wiki/Mutual_exclusion
83/// [RAII guards]: MutexGuard
84/// [`lock`]: Self::lock
85/// [`try_lock`]: Self::try_lock
86/// [task]: core::task
87/// [fairly queued]: https://en.wikipedia.org/wiki/Unbounded_nondeterminism#Fairness
88/// [`std::sync::Mutex`]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html
89/// [`blocking::Mutex`]: crate::blocking::Mutex
90/// [`futures-util`]: https://crates.io/crate/futures-util
91/// [`futures_util::lock::Mutex`]: https://docs.rs/futures-util/latest/futures_util/lock/struct.Mutex.html
92/// [intrusive linked list]: crate::WaitQueue#implementation-notes
93/// [poisoning]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html#poisoning
94// for some reason, intra-doc links don't work in footnotes?
95/// [storage]: https://mycelium.elizas.website/maitake/task/trait.Storage.html
96/// [no-unwinding]: https://mycelium.elizas.website/maitake/index.html#maitake-does-not-support-unwinding
97pub struct Mutex<T: ?Sized, L: ScopedRawMutex = DefaultMutex> {
98    wait: WaitQueue<L>,
99    data: UnsafeCell<T>,
100}
101
102/// An [RAII] implementation of a "scoped lock" of a [`Mutex`]. When this
103/// structure is dropped (falls out of scope), the lock will be unlocked.
104///
105/// The data protected by the mutex can be accessed through this guard via its
106/// [`Deref`](#impl-Deref) and [`DerefMut`](#impl-Deref) implementations.
107///
108/// This guard can be held across any `.await` point, as it implements
109/// [`Send`].
110///
111/// This structure is created by the [`lock`] and [`try_lock`] methods on
112/// [`Mutex`].
113///
114/// [`lock`]: Mutex::lock
115/// [`try_lock`]: Mutex::try_lock
116/// [RAII]: https://rust-unofficial.github.io/patterns/patterns/behavioural/RAII.html
117#[must_use = "if unused, the `Mutex` will immediately unlock"]
118pub struct MutexGuard<'a, T: ?Sized, L: ScopedRawMutex = DefaultMutex> {
119    /// /!\ WARNING: semi-load-bearing drop order /!\
120    ///
121    /// This struct's field ordering is important.
122    data: MutPtr<T>,
123    _wake: WakeOnDrop<'a, T, L>,
124}
125
126/// A [future] returned by the [`Mutex::lock`] method.
127///
128/// [future]: core::future::Future
129///
130/// # Notes
131///
132/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a
133/// `Lock` future once it has been polled. For instance, the following code
134/// must not compile:
135///
136///```compile_fail
137/// use maitake_sync::mutex::Lock;
138///
139/// // Calls to this function should only compile if `T` is `Unpin`.
140/// fn assert_unpin<T: Unpin>() {}
141///
142/// assert_unpin::<Lock<'_, ()>>();
143/// ```
144#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
145#[pin_project]
146#[derive(Debug)]
147pub struct Lock<'a, T: ?Sized, L: ScopedRawMutex = DefaultMutex> {
148    #[pin]
149    wait: wait_queue::Wait<'a, L>,
150    mutex: &'a Mutex<T, L>,
151}
152
153/// This is used in order to ensure that the wakeup is performed only *after*
154/// the data ptr is dropped, in order to keep `loom` happy.
155struct WakeOnDrop<'a, T: ?Sized, L: ScopedRawMutex>(&'a Mutex<T, L>);
156
157// === impl Mutex ===
158
159impl<T> Mutex<T> {
160    loom_const_fn! {
161        /// Returns a new `Mutex` protecting the provided `data`.
162        ///
163        /// The returned `Mutex` will be in the unlocked state and is ready for
164        /// use.
165        ///
166        /// This constructor returns a [`Mutex`] that uses a [`DefaultMutex`] as the
167        /// underlying blocking mutex implementation. To use an alternative
168        /// [`ScopedRawMutex`] implementation, use the [`Mutex::new_with_raw_mutex`]
169        /// constructor instead. See [the documentation on overriding mutex
170        /// implementations](crate::blocking#overriding-mutex-implementations)
171        /// for more details.
172        ///
173        /// # Examples
174        ///
175        /// ```
176        /// use maitake_sync::Mutex;
177        ///
178        /// let lock = Mutex::new(42);
179        /// ```
180        ///
181        /// As this is a `const fn`, it may be used in a `static` initializer:
182        /// ```
183        /// use maitake_sync::Mutex;
184        ///
185        /// static GLOBAL_LOCK: Mutex<usize> = Mutex::new(42);
186        /// ```
187        #[must_use]
188        pub fn new(data: T) -> Self {
189            Self::new_with_raw_mutex(data, DefaultMutex::new())
190        }
191    }
192}
193
194impl<T, L: ScopedRawMutex> Mutex<T, L> {
195    loom_const_fn! {
196        /// Returns a new `Mutex` protecting the provided `data`, using the provided
197        /// [`ScopedRawMutex`] implementation as the raw mutex.
198        ///
199        /// The returned `Mutex` will be in the unlocked state and is ready for
200        /// use.
201        ///
202        /// This constructor allows a [`Mutex`] to be constructed with any type that
203        /// implements [`ScopedRawMutex`] as the underlying raw blocking mutex
204        /// implementation. See [the documentation on overriding mutex
205        /// implementations](crate::blocking#overriding-mutex-implementations)
206        /// for more details.
207        pub fn new_with_raw_mutex(data: T, lock: L) -> Self {
208            Self {
209                // The queue must start with a single stored wakeup, so that the
210                // first task that tries to acquire the lock will succeed
211                // immediately.
212                wait: WaitQueue::<L>::new_woken(lock),
213                data: UnsafeCell::new(data),
214            }
215        }
216    }
217}
218
219impl<T, L: ScopedRawMutex> Mutex<T, L> {
220    /// Consumes this `Mutex`, returning the guarded data.
221    #[inline]
222    #[must_use]
223    pub fn into_inner(self) -> T {
224        self.data.into_inner()
225    }
226}
227
228impl<T: ?Sized, L: ScopedRawMutex> Mutex<T, L> {
229    /// Locks this mutex.
230    ///
231    /// This returns a [`Lock`] future that will wait until no other task is
232    /// accessing the shared data. If the shared data is not locked, this future
233    /// will complete immediately. When the lock has been acquired, this future
234    /// will return a [`MutexGuard`].
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// use maitake_sync::Mutex;
240    ///
241    /// async fn example() {
242    ///     let mutex = Mutex::new(1);
243    ///
244    ///     let mut guard = mutex.lock().await;
245    ///     *guard = 2;
246    /// }
247    /// ```
248    pub fn lock(&self) -> Lock<'_, T, L> {
249        Lock {
250            wait: self.wait.wait(),
251            mutex: self,
252        }
253    }
254
255    /// Attempts to lock the mutex without waiting, returning `None` if the
256    /// mutex is already locked locked.
257    ///
258    /// # Returns
259    ///
260    /// - `Some(`[`MutexGuard`])` if the mutex was not already locked
261    /// - `None` if the mutex is currently locked and locking it would require
262    ///   waiting
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use maitake_sync::Mutex;
268    /// # async fn dox() -> Option<()> {
269    ///
270    /// let mutex = Mutex::new(1);
271    ///
272    /// let n = mutex.try_lock()?;
273    /// assert_eq!(*n, 1);
274    /// # Some(())
275    /// # }
276    /// ```
277    pub fn try_lock(&self) -> Option<MutexGuard<'_, T, L>> {
278        match self.wait.try_wait() {
279            Poll::Pending => None,
280            Poll::Ready(Ok(_)) => Some(unsafe {
281                // safety: we have just acquired the lock
282                self.guard()
283            }),
284            Poll::Ready(Err(_)) => unsafe {
285                unreachable_unchecked!("`Mutex` never calls `WaitQueue::close`")
286            },
287        }
288    }
289
290    /// Returns a mutable reference to the underlying data.
291    ///
292    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
293    /// take place -- the mutable borrow statically guarantees no locks exist.
294    pub fn get_mut(&mut self) -> &mut T {
295        unsafe {
296            // Safety: since this call borrows the `Mutex` mutably, no actual
297            // locking needs to take place -- the mutable borrow statically
298            // guarantees no locks exist.
299            self.data.with_mut(|data| &mut *data)
300        }
301    }
302
303    /// Constructs a new `MutexGuard` for this `Mutex`.
304    ///
305    /// # Safety
306    ///
307    /// This may only be called once a lock has been acquired.
308    unsafe fn guard(&self) -> MutexGuard<'_, T, L> {
309        MutexGuard {
310            _wake: WakeOnDrop(self),
311            data: self.data.get_mut(),
312        }
313    }
314}
315
316impl<T: Default> Default for Mutex<T> {
317    fn default() -> Self {
318        Self::new(Default::default())
319    }
320}
321
322impl<T, L> fmt::Debug for Mutex<T, L>
323where
324    T: ?Sized + fmt::Debug,
325    L: ScopedRawMutex + fmt::Debug,
326{
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        let Self { data: _, wait } = self;
329        f.debug_struct("Mutex")
330            .field("data", &fmt::opt(&self.try_lock()).or_else("<locked>"))
331            .field("wait", wait)
332            .finish()
333    }
334}
335
336unsafe impl<T, L: ScopedRawMutex> Send for Mutex<T, L>
337where
338    T: ?Sized + Send,
339    L: Send,
340{
341}
342unsafe impl<T, L: ScopedRawMutex> Sync for Mutex<T, L>
343where
344    T: ?Sized + Send,
345    L: Sync,
346{
347}
348
349// === impl Lock ===
350
351impl<'a, T, L> Future for Lock<'a, T, L>
352where
353    T: ?Sized,
354    L: ScopedRawMutex,
355{
356    type Output = MutexGuard<'a, T, L>;
357
358    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
359        let this = self.project();
360
361        match this.wait.poll(cx) {
362            Poll::Ready(Ok(())) => {}
363            Poll::Ready(Err(_)) => unsafe {
364                unreachable_unchecked!("`Mutex` never calls `WaitQueue::close`")
365            },
366            Poll::Pending => return Poll::Pending,
367        }
368
369        let guard = unsafe {
370            // safety: we have just acquired the lock.
371            this.mutex.guard()
372        };
373        Poll::Ready(guard)
374    }
375}
376
377// === impl MutexGuard ===
378
379impl<T, L> Deref for MutexGuard<'_, T, L>
380where
381    T: ?Sized,
382    L: ScopedRawMutex,
383{
384    type Target = T;
385
386    #[inline]
387    fn deref(&self) -> &Self::Target {
388        unsafe {
389            // safety: we are holding the lock
390            &*self.data.deref()
391        }
392    }
393}
394
395impl<T, L> DerefMut for MutexGuard<'_, T, L>
396where
397    T: ?Sized,
398    L: ScopedRawMutex,
399{
400    #[inline]
401    fn deref_mut(&mut self) -> &mut Self::Target {
402        unsafe {
403            // safety: we are holding the lock
404            self.data.deref()
405        }
406    }
407}
408
409impl<T, L> fmt::Debug for MutexGuard<'_, T, L>
410where
411    T: ?Sized + fmt::Debug,
412    L: ScopedRawMutex,
413{
414    #[inline]
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        self.deref().fmt(f)
417    }
418}
419
420unsafe impl<T, L> Send for MutexGuard<'_, T, L>
421where
422    T: ?Sized + Send,
423    L: ScopedRawMutex + Sync,
424{
425}
426unsafe impl<T, L> Sync for MutexGuard<'_, T, L>
427where
428    T: ?Sized + Send + Sync,
429    // A `MutexGuard`` has a reference to a `L`-typed ScopedRawMutex in it, so ``
430    L: ScopedRawMutex + Sync,
431{
432}
433
434impl<T: ?Sized, L: ScopedRawMutex> Drop for WakeOnDrop<'_, T, L> {
435    fn drop(&mut self) {
436        self.0.wait.wake()
437    }
438}
439
440feature! {
441    #![feature = "alloc"]
442
443    use alloc::sync::Arc;
444
445    /// An [RAII] implementation of a "scoped lock" of a [`Mutex`]. When this
446    /// structure is dropped (falls out of scope), the lock will be unlocked.
447    ///
448    /// This type is similar to the [`MutexGuard`] type, but it is only returned
449    /// by a [`Mutex`] that is wrapped in an an [`Arc`]. Instead of borrowing
450    /// the [`Mutex`], this guard holds an [`Arc`] clone of the [`Mutex`],
451    /// incrementing its reference count. Therefore, this type can outlive the
452    /// [`Mutex`] that created it, and it is valid for the `'static` lifetime.
453    ///
454    /// The data protected by the mutex can be accessed through this guard via its
455    /// [`Deref`](#impl-Deref) and [`DerefMut`](#impl-Deref) implementations.
456    ///
457    /// This guard can be held across any `.await` point, as it implements
458    /// [`Send`].
459    ///
460    /// This structure is created by the [`lock_owned`] and [`try_lock_owned`]
461    /// methods on  [`Mutex`].
462    ///
463    /// [`lock_owned`]: Mutex::lock_owned
464    /// [`try_lock_owned`]: Mutex::try_lock_owned
465    /// [RAII]: https://rust-unofficial.github.io/patterns/patterns/behavioural/RAII.html
466    #[must_use = "if unused, the Mutex will immediately unlock"]
467    pub struct OwnedMutexGuard<T: ?Sized, L: ScopedRawMutex> {
468        /// /!\ WARNING: semi-load-bearing drop order /!\
469        ///
470        /// This struct's field ordering is important.
471        data: MutPtr<T>,
472        _wake: WakeArcOnDrop<T, L>,
473    }
474
475    impl<T: ?Sized, L: ScopedRawMutex> Mutex<T, L> {
476
477        /// Locks this mutex, returning an [owned RAII guard][`OwnedMutexGuard`].
478        ///
479        /// This function will that will wait until no other task is
480        /// accessing the shared data. If the shared data is not locked, this future
481        /// will complete immediately. When the lock has been acquired, this future
482        /// will return a [`OwnedMutexGuard`].
483        ///
484        /// This method is similar to [`Mutex::lock`], except that (rather
485        /// than borrowing the [`Mutex`]) the returned  guard owns an [`Arc`]
486        /// clone, incrememting its reference count. Therefore, this method is
487        /// only available when the [`Mutex`] is wrapped in an [`Arc`], and the
488        /// returned guard is valid for the `'static` lifetime.
489        ///
490        /// # Examples
491        ///
492        /// ```
493        /// # // since we are targeting no-std, it makes more sense to use `alloc`
494        /// # // in these examples, rather than `std`...but i don't want to make
495        /// # // the tests actually `#![no_std]`...
496        /// # use std as alloc;
497        /// use maitake_sync::Mutex;
498        /// use alloc::sync::Arc;
499        ///
500        /// # fn main() {
501        /// async fn example() {
502        ///     let mutex = Arc::new(Mutex::new(1));
503        ///
504        ///     let mut guard = mutex.clone().lock_owned().await;
505        ///     *guard = 2;
506        ///     # drop(mutex);
507        /// }
508        /// # }
509        /// ```
510        pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T, L> {
511            self.wait.wait().await.unwrap();
512            unsafe {
513                // safety: we have just acquired the lock
514                self.owned_guard()
515            }
516        }
517
518        /// Attempts this mutex without waiting, returning an [owned RAII
519        /// guard][`OwnedMutexGuard`], or `Err` if the mutex is already locked.
520        ///
521        /// This method is similar to [`Mutex::try_lock`], except that (rather
522        /// than borrowing the [`Mutex`]) the returned guard owns an [`Arc`]
523        /// clone, incrememting its reference count. Therefore, this method is
524        /// only available when the [`Mutex`] is wrapped in an [`Arc`], and the
525        /// returned guard is valid for the `'static` lifetime.
526        ///
527        /// # Returns
528        ///
529        /// - `Ok(`[`OwnedMutexGuard`])` if the mutex was not already locked
530        /// - `Err(Arc<Mutex<T>>)` if the mutex is currently locked and locking
531        ///   it would require waiting.
532        ///
533        ///   This returns an [`Err`] rather than [`None`] so that the same
534        ///   [`Arc`] clone may be reused (such as by calling `try_lock_owned`
535        ///   again) without having to decrement and increment the reference
536        ///   count again.
537        ///
538        /// # Examples
539        ///
540        /// ```
541        /// # // since we are targeting no-std, it makes more sense to use `alloc`
542        /// # // in these examples, rather than `std`...but i don't want to make
543        /// # // the tests actually `#![no_std]`...
544        /// # use std as alloc;
545        /// use maitake_sync::Mutex;
546        /// use alloc::sync::Arc;
547        ///
548        /// # fn main() {
549        /// let mutex = Arc::new(Mutex::new(1));
550        ///
551        /// if let Ok(guard) = mutex.clone().try_lock_owned() {
552        ///     assert_eq!(*guard, 1);
553        /// }
554        /// # }
555        /// ```
556        pub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T, L>, Arc<Self>> {
557            match self.wait.try_wait() {
558                Poll::Pending => Err(self),
559                Poll::Ready(Ok(_)) => Ok(unsafe {
560                    // safety: we have just acquired the lock
561                    self.owned_guard()
562                }),
563                Poll::Ready(Err(_)) => unsafe {
564                    unreachable_unchecked!("`Mutex` never calls `WaitQueue::close`")
565                },
566            }
567        }
568
569        /// Constructs a new `OwnedMutexGuard` for this `Mutex`.
570        ///
571        /// # Safety
572        ///
573        /// This may only be called once a lock has been acquired.
574        unsafe fn owned_guard(self: Arc<Self>) -> OwnedMutexGuard<T, L> {
575            let data = self.data.get_mut();
576            OwnedMutexGuard {
577                _wake: WakeArcOnDrop(self),
578                data,
579            }
580        }
581    }
582
583    struct WakeArcOnDrop<T: ?Sized, L: ScopedRawMutex>(Arc<Mutex<T, L>>);
584
585    // === impl OwnedMutexGuard ===
586
587    impl<T, L> Deref for OwnedMutexGuard<T, L>
588    where
589        T: ?Sized,
590        L: ScopedRawMutex,
591    {
592        type Target = T;
593
594        #[inline]
595        fn deref(&self) -> &Self::Target {
596            unsafe {
597                // safety: we are holding the lock
598                &*self.data.deref()
599            }
600        }
601    }
602
603    impl<T, L> DerefMut for OwnedMutexGuard<T, L>
604    where
605        T: ?Sized,
606        L: ScopedRawMutex,
607    {
608        #[inline]
609        fn deref_mut(&mut self) -> &mut Self::Target {
610            unsafe {
611                // safety: we are holding the lock
612                self.data.deref()
613            }
614        }
615    }
616
617    impl<T, L> fmt::Debug for OwnedMutexGuard<T, L>
618    where
619        T: ?Sized + fmt::Debug,
620        L: ScopedRawMutex,
621    {
622        #[inline]
623        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624            self.deref().fmt(f)
625        }
626    }
627
628    unsafe impl<T, L> Send for OwnedMutexGuard<T, L>
629    where
630        T: ?Sized + Send,
631        L: ScopedRawMutex + Sync,
632    {
633    }
634    unsafe impl<T, L> Sync for OwnedMutexGuard<T, L>
635    where
636        T: ?Sized + Send + Sync,
637        L: ScopedRawMutex + Sync,
638    {
639    }
640
641    impl<T, L> Drop for WakeArcOnDrop<T, L>
642    where
643        T: ?Sized,
644        L: ScopedRawMutex,
645    {
646        fn drop(&mut self) {
647            self.0.wait.wake()
648        }
649    }
650}