deallocate_zeroed/zero_aware_allocator/
mutex.rs

1//! Provides a mutex container type similar to `std::sync::Mutex<T>` but
2//! parameterized over any type `L` that implements the `Lock` trait, for better
3//! no-std support.
4
5use core::{
6    cell::{Cell, UnsafeCell},
7    ops::{Deref, DerefMut},
8};
9
10/// A trait for providing mutual exclusion.
11///
12/// If you do not need to use the allocator, and collections using it, in a
13/// multi-threaded environment, you may use [`SingleThreadedLockingMechanism`],
14/// which is the moral equivalent of a `RefCell`.
15///
16/// # Safety
17///
18/// * If the implementation type is `Sync`, then an allocator using this locking
19///   mechanism will be `Sync`, and therefore this method must provide actual
20///   mutual exclusion and prevent against unsynchronized accesses.
21///
22/// * Even in single-threaded contexts, where real synchronization is not
23///   required, this type must prevent recursive locking and re-entering the
24///   lock when it is already held. The prevention may be a panic, abort,
25///   infinite loop, or etc...
26pub unsafe trait LockingMechanism {
27    /// Lock this mutex.
28    ///
29    /// If it is already locked, this must result in a panic, abort, infinite
30    /// loop, or etc... and locking must not succeed.
31    fn lock(&self);
32
33    /// Unlock this mutex.
34    fn unlock(&self);
35}
36
37/// A single-threaded implementation of [`LockingMechanism`].
38///
39/// This is effectively a `RefCell`. It allows using the `ZeroAwareAllocator` in
40/// single-threaded scenarios.
41#[derive(Debug)]
42pub struct SingleThreadedLockingMechanism {
43    locked: Cell<bool>,
44}
45
46/// ```compile_fail
47/// use deallocate_zeroed::*;
48/// fn assert_sync<S: Sync>() {}
49/// assert_sync::<SingleThreadedLockingMechanism>();
50/// ```
51#[cfg(doctest)]
52struct _SingleThreadedLockingMechanismIsNotSync;
53
54unsafe impl LockingMechanism for SingleThreadedLockingMechanism {
55    #[inline]
56    fn lock(&self) {
57        assert!(!self.locked.get());
58        self.locked.set(true);
59    }
60
61    #[inline]
62    fn unlock(&self) {
63        assert!(self.locked.get());
64        self.locked.set(false);
65    }
66}
67
68impl Default for SingleThreadedLockingMechanism {
69    #[inline]
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl SingleThreadedLockingMechanism {
76    /// Construct a new `SingleThreadedLockingMechanism`.
77    #[inline]
78    pub const fn new() -> Self {
79        SingleThreadedLockingMechanism {
80            locked: Cell::new(false),
81        }
82    }
83}
84
85/// Similar to `std::sync::Mutex<T>` but built on top of [`LockingMechanism`].
86#[derive(Default)]
87pub struct Mutex<T, L> {
88    lock: L,
89    value: UnsafeCell<T>,
90}
91
92// Safety: if `T` and `L` can be sent between threads, then the mutex can as
93// well. The API, implementation, and borrow checker do not allow for
94// unsynchronized accesses in the face of sending these across threads.
95unsafe impl<T, L> Send for Mutex<T, L>
96where
97    T: Send,
98    L: Send,
99{
100}
101
102// Safety: upheld by the `LockingMechanism` trait's implementation contract.
103//
104// Additionally, `T` must be `Send` because locking a mutex from another thread
105// and getting a mutex guard allows getting `&mut T`, which can be used to
106// `mem::replace()` the `T`, effectively sending it between threads.
107unsafe impl<T, L> Sync for Mutex<T, L>
108where
109    T: Send,
110    L: Sync + LockingMechanism,
111{
112}
113
114impl<T, L> Mutex<T, L>
115where
116    L: LockingMechanism,
117{
118    /// Construct a new `Mutex` with the given locking mechanism.
119    pub const fn new(value: T, lock: L) -> Self {
120        let value = UnsafeCell::new(value);
121        Mutex { lock, value }
122    }
123
124    /// Lock this `Mutex`.
125    pub fn lock(&self) -> MutexGuard<'_, T, L> {
126        self.lock.lock();
127        MutexGuard { mutex: self }
128    }
129}
130
131/// Like `std::sync::MutexGuard<T>` but built on top of [`LockingMechanism`].
132pub struct MutexGuard<'a, T, L>
133where
134    L: LockingMechanism,
135{
136    mutex: &'a Mutex<T, L>,
137}
138
139impl<'a, T, L> Drop for MutexGuard<'a, T, L>
140where
141    L: LockingMechanism,
142{
143    fn drop(&mut self) {
144        self.mutex.lock.unlock();
145    }
146}
147
148impl<T, L> Deref for MutexGuard<'_, T, L>
149where
150    L: LockingMechanism,
151{
152    type Target = T;
153
154    #[inline]
155    fn deref(&self) -> &Self::Target {
156        unsafe { &*self.mutex.value.get() }
157    }
158}
159
160impl<T, L> DerefMut for MutexGuard<'_, T, L>
161where
162    L: LockingMechanism,
163{
164    fn deref_mut(&mut self) -> &mut Self::Target {
165        unsafe { &mut *self.mutex.value.get() }
166    }
167}