1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
/// Const Init Trait
///
/// This trait is intended for use when implementers of [`ScopedRawMutex`] that can
/// be constructed in const context, e.g. for placing in a `static`
pub trait ConstInit {
/// Create a new instance.
///
/// This is a const instead of a method to allow creating instances in const context.
const INIT: Self;
}
/// Raw scoped mutex trait.
///
/// This mutex is "raw", which means it does not actually contain the protected data, it
/// just implements the mutex mechanism. For most uses you should use `BlockingMutex`
/// from the `scoped-mutex-impls` crate instead, which is generic over a
/// `ScopedRawMutex` and contains the protected data.
///
/// # Safety
///
/// ScopedRawMutex implementations must ensure that, while locked, no other thread can lock
/// the RawMutex concurrently. This can usually be implemented using an [`AtomicBool`]
/// to track the "taken" state. See the `scoped-mutex-impls` crate for examples of
/// correct implementations.
///
/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior.
///
/// [`AtomicBool`]: core::sync::atomic::AtomicBool
pub unsafe trait ScopedRawMutex {
/// Lock this `ScopedRawMutex`, calling `f()` after the lock has been acquired, and releasing
/// the lock after the completion of `f()`.
///
/// If this was successful, `Some(R)` will be returned. If the mutex was already locked,
/// `None` will be returned
#[must_use]
fn try_with_lock<R>(&self, f: impl FnOnce() -> R) -> Option<R>;
/// Lock this `ScopedRawMutex`, calling `f()` after the lock has been acquired, and releasing
/// the lock after the completion of `f()`.
///
/// Implementors may choose whether to block or panic if the lock is already locked.
/// It is recommended to panic if it is possible to know that deadlock has occurred.
///
/// For implementations on a system with threads, blocking may be the correct choice.
///
/// For implementations where a single thread is present, panicking immediately may be
/// the correct choice.
fn with_lock<R>(&self, f: impl FnOnce() -> R) -> R;
/// Is this mutex currently locked?
fn is_locked(&self) -> bool;
}
/// Raw mutex trait.
///
/// This trait represents an implementation of a generic mutual-exclusion lock
/// which may be locked and unlocked freely at any time.
///
/// This mutex is "raw", which means it does not actually contain the protected
/// data, it just implements the mutex mechanism. For most uses you should use
/// `BlockingMutex` from the `mutex` crate instead, which is generic over a
/// `RawMutex` and contains the protected data.
///
/// # `RawMutex` and [`ScopedRawMutex`]
///
/// The `RawMutex` trait is a superset of the [`ScopedRawMutex`] trait. The
/// interface defined in [`ScopedRawMutex`] is more restrictive, and only
/// permits the mutex to be locked for the duration of a single [`FnOnce`] call
/// and unlocked immediately when that closure exits. `RawMutex`, on the other
/// hand, permits a much wider range of potential usage patterns: it may be used
/// to implement a RAII-style lock guard like [`std::sync::Mutex`][s], a
/// "C-style" mutex where explicit `lock` and `unlock` calls have to be paired
/// manually, *or* a scoped closure-based API like [`ScopedRawMutex`].
/// Therefore, **there is [a blanket implementation][blanket] of
/// [`ScopedRawMutex`] for all types that implement `RawMutex`**.
///
/// Some mutex implementations may not be able to implement the full `RawMutex`
/// trait, and may only be able to implement the closure-based
/// [`ScopedRawMutex`] subset. For example, implementations for the
/// [`critical-section`] crate (in [`mutex::raw_impls::cs`][cs]) can only
/// implement the `ScopedRawMutex` trait. However, in general, **mutex
/// implementations that *can* implement the more general `RawMutex` trait
/// should prefer to do so**, as they will be able to be used in code that
/// requires either interface.
///
/// # Safety
///
/// Implementations of this trait must ensure that the mutex is actually
/// exclusive: a lock can't be acquired while the mutex is already locked.
///
/// [blanket]: ScopedRawMutex#impl-ScopedRawMutex-for-M
/// [s]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html
/// [cs]: https://docs.rs/mutex/latest/mutex/raw_impls/cs/index.html
/// [critical-section]: https://docs.rs/critical-section/latest/critical_section/
pub unsafe trait RawMutex {
/// Marker type which determines whether a lock guard should be [`Send`].
type GuardMarker;
/// Acquires this mutex, blocking the current thread/CPU core until it is
/// able to do so.
fn lock(&self);
/// Attempts to acquire this mutex without blocking. Returns `true`
/// if the lock was successfully acquired and `false` otherwise.
fn try_lock(&self) -> bool;
/// Unlocks this mutex.
///
/// # Safety
///
/// This method may only be called if the mutex is held in the current
/// context, i.e. it must be paired with a successful call to [`lock`] or
/// [`try_lock`].
///
/// [`lock`]: RawMutex::lock
/// [`try_lock`]: RawMutex::try_lock
unsafe fn unlock(&self);
/// Returns `true` if the mutex is currently locked.
fn is_locked(&self) -> bool;
}
unsafe impl<M: RawMutex> ScopedRawMutex for M {
#[must_use]
#[inline]
#[track_caller]
fn try_with_lock<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
if self.try_lock() {
// Using a drop guard ensures that the mutex is unlocked when this
// function exits, even if `f()` panics.
let _unlock = Unlock(self);
Some(f())
} else {
None
}
}
#[inline]
#[track_caller]
fn with_lock<R>(&self, f: impl FnOnce() -> R) -> R {
self.lock();
// Using a drop guard ensures that the mutex is unlocked when this
// function exits, even if `f()` panics.
let _unlock = Unlock(self);
f()
}
/// Is this mutex currently locked?
#[inline]
fn is_locked(&self) -> bool {
RawMutex::is_locked(self)
}
}
/// Implementation detail of the `ScopedRawMutex` implementation for `RawMutex`.
/// This is a drop guard that unlocks the `RawMutex` when it's dropped. This is
/// used to ensure that the `RawMutex` is always unlocked when the
/// `ScopedRawMutex::with_lock` or `ScopedRawMutex::try_with_lock` closures are
/// exited, even if they are exited by a panic rather than by a normal return.
struct Unlock<'mutex, M: RawMutex>(&'mutex M);
impl<M: RawMutex> Drop for Unlock<'_, M> {
fn drop(&mut self) {
unsafe {
// Safety: Constructing an `Unlock` is only safe if the mutex has
// been locked. Callers are responsible for ensuring this invariant;
// since this struct is only constructed in this module, we do so.
self.0.unlock()
}
}
}