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}