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}