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()
        }
    }
}