nuclei/sync/
reentrant_mutex.rs1#![allow(unsafe_code)]
5
6use core::fmt;
7use core::future::Future;
8use core::marker::PhantomData;
9use core::ops::Deref;
10use core::pin::Pin;
11use core::task::{Context, Poll};
12
13use super::internal::{AtomicU64, Ordering, UnsafeCell};
14use super::owner::Owner;
15use super::raw_semaphore::{PermitWait, RawSemaphore};
16
17pub struct ReentrantMutex<T: ?Sized> {
41 sem: RawSemaphore,
42 holder: AtomicU64,
45 depth: UnsafeCell<usize>,
47 value: core::cell::UnsafeCell<T>,
48}
49
50unsafe impl<T: ?Sized + Send> Send for ReentrantMutex<T> {}
52unsafe impl<T: ?Sized + Send> Sync for ReentrantMutex<T> {}
56
57impl<T> ReentrantMutex<T> {
58 loom_const_fn! {
59 pub fn new(value: T) -> Self {
61 Self {
62 sem: RawSemaphore::new(1),
63 holder: AtomicU64::new(0),
64 depth: UnsafeCell::new(0),
65 value: core::cell::UnsafeCell::new(value),
66 }
67 }
68 }
69
70 pub fn into_inner(self) -> T {
72 self.value.into_inner()
73 }
74}
75
76impl<T: ?Sized> ReentrantMutex<T> {
77 pub fn try_lock<'a>(&'a self, owner: &'a Owner) -> Option<ReentrantMutexGuard<'a, T>> {
80 if self.reenter(owner) || self.sem.try_acquire(1) && self.enter(owner) {
81 return Some(ReentrantMutexGuard::new(self));
82 }
83 None
84 }
85
86 pub fn lock<'a>(&'a self, owner: &'a Owner) -> ReentrantMutexLock<'a, T> {
90 ReentrantMutexLock {
91 mutex: self,
92 owner,
93 wait: PermitWait::new(),
94 }
95 }
96
97 pub fn get_mut(&mut self) -> &mut T {
99 self.value.get_mut()
100 }
101
102 #[expect(
104 clippy::panic,
105 reason = "a count that cannot overflow in practice, checked rather than wrapped"
106 )]
107 fn reenter(&self, owner: &Owner) -> bool {
108 if self.holder.load(Ordering::Relaxed) != owner.id() {
109 return false;
110 }
111 self.depth.with_mut(|depth| unsafe {
113 *depth = (*depth)
114 .checked_add(1)
115 .unwrap_or_else(|| panic!("a ReentrantMutex was locked usize::MAX times"));
116 });
117 true
118 }
119
120 fn enter(&self, owner: &Owner) -> bool {
122 self.depth.with_mut(|depth| unsafe { *depth = 1 });
124 self.holder.store(owner.id(), Ordering::Relaxed);
125 true
126 }
127
128 fn unlock(&self) {
129 let left = self.depth.with_mut(|depth| unsafe {
132 *depth -= 1;
133 *depth
134 });
135 if left == 0 {
136 self.holder.store(0, Ordering::Relaxed);
137 self.sem.release(1);
138 }
139 }
140}
141
142impl<T: Default> Default for ReentrantMutex<T> {
143 fn default() -> Self {
144 Self::new(T::default())
145 }
146}
147
148impl<T: ?Sized> fmt::Debug for ReentrantMutex<T> {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 f.debug_struct("ReentrantMutex").finish_non_exhaustive()
151 }
152}
153
154#[must_use = "dropping the guard releases one level of the lock at once"]
160pub struct ReentrantMutexGuard<'a, T: ?Sized> {
161 mutex: &'a ReentrantMutex<T>,
162 _owner: PhantomData<&'a Owner>,
165}
166
167impl<'a, T: ?Sized> ReentrantMutexGuard<'a, T> {
168 fn new(mutex: &'a ReentrantMutex<T>) -> Self {
169 Self {
170 mutex,
171 _owner: PhantomData,
172 }
173 }
174}
175
176impl<T: ?Sized> Deref for ReentrantMutexGuard<'_, T> {
177 type Target = T;
178
179 fn deref(&self) -> &T {
180 unsafe { &*self.mutex.value.get() }
183 }
184}
185
186impl<T: ?Sized> Drop for ReentrantMutexGuard<'_, T> {
187 fn drop(&mut self) {
188 self.mutex.unlock();
189 }
190}
191
192impl<T: ?Sized + fmt::Debug> fmt::Debug for ReentrantMutexGuard<'_, T> {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 fmt::Debug::fmt(&**self, f)
195 }
196}
197
198#[must_use = "futures do nothing unless polled"]
200pub struct ReentrantMutexLock<'a, T: ?Sized> {
201 mutex: &'a ReentrantMutex<T>,
202 owner: &'a Owner,
203 wait: PermitWait,
204}
205
206impl<'a, T: ?Sized> Future for ReentrantMutexLock<'a, T> {
207 type Output = ReentrantMutexGuard<'a, T>;
208
209 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<ReentrantMutexGuard<'a, T>> {
210 let this = self.get_mut();
211 if !this.wait.is_queued() && this.mutex.reenter(this.owner) {
212 let guard = ReentrantMutexGuard::new(this.mutex);
215 if crate::coop::spend(cx.waker()) {
216 return Poll::Ready(guard);
217 }
218 drop(guard);
219 return Poll::Pending;
220 }
221 this.wait.poll(&this.mutex.sem, 1, cx.waker()).map(|()| {
222 this.mutex.enter(this.owner);
223 ReentrantMutexGuard::new(this.mutex)
224 })
225 }
226}
227
228impl<T: ?Sized> Drop for ReentrantMutexLock<'_, T> {
229 fn drop(&mut self) {
230 self.wait.cancel(&self.mutex.sem, 1);
231 }
232}
233
234impl<T: ?Sized> fmt::Debug for ReentrantMutexLock<'_, T> {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 f.debug_struct("ReentrantMutexLock")
237 .field("owner", self.owner)
238 .finish_non_exhaustive()
239 }
240}
241
242#[cfg(all(test, not(loom)))]
243mod tests {
244 use super::*;
245 use crate::sync::test_support::Polled;
246 use core::cell::RefCell;
247
248 #[test]
249 fn the_holder_reenters_and_the_lock_opens_after_its_last_guard() {
250 let lock = ReentrantMutex::new(RefCell::new(Vec::new()));
251 let (me, other) = (Owner::new(), Owner::new());
252 let outer = lock.try_lock(&me).expect("free");
253 let inner = Polled::new(lock.lock(&me)).ready();
254 inner.borrow_mut().push(1);
255 let mut waiting = Polled::new(lock.lock(&other));
256 waiting.pending();
257 assert!(lock.try_lock(&other).is_none());
258 drop(inner);
259 assert_eq!(waiting.wakes(), 0, "one level is still held");
260 let again = lock.try_lock(&me).expect("still the holder");
261 drop((outer, again));
262 assert_eq!(waiting.wakes(), 1);
263 let theirs = waiting.ready();
264 theirs.borrow_mut().push(2);
265 assert!(lock.try_lock(&me).is_none(), "the lock changed hands");
266 drop(theirs);
267 drop(waiting);
268 assert_eq!(lock.into_inner().into_inner(), vec![1, 2]);
269 }
270
271 #[test]
272 fn a_thread_owner_is_the_same_on_one_thread_and_different_on_another() {
273 let lock = ReentrantMutex::new(());
274 let here = Owner::thread();
275 let guard = lock.try_lock(&here).expect("free");
276 let again = Owner::thread();
277 assert!(lock.try_lock(&again).is_some(), "the same thread re-enters");
278 std::thread::scope(|scope| {
279 scope.spawn(|| {
280 let there = Owner::thread();
281 assert!(
282 lock.try_lock(&there).is_none(),
283 "another thread is another owner"
284 );
285 });
286 });
287 drop(guard);
288 }
289
290 #[test]
291 fn a_withdrawn_lock_future_leaves_no_trace() {
292 let lock = ReentrantMutex::new(());
293 let (me, other) = (Owner::new(), Owner::new());
294 let held = lock.try_lock(&me).expect("free");
295 Polled::new(lock.lock(&other)).pending();
296 drop(held);
297 assert!(lock.try_lock(&other).is_some());
298 }
299}