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}