1use core::sync::atomic::{AtomicI16, Ordering};
12use spin::{Mutex, MutexGuard};
13
14const FREE: i16 = -1;
16
17pub struct RecoverableMutex<T> {
18 inner: Mutex<T>,
19 holder: AtomicI16,
21}
22
23unsafe impl<T: Send> Sync for RecoverableMutex<T> {}
24unsafe impl<T: Send> Send for RecoverableMutex<T> {}
25
26impl<T> RecoverableMutex<T> {
27 pub const fn new(value: T) -> Self {
28 Self {
29 inner: Mutex::new(value),
30 holder: AtomicI16::new(FREE),
31 }
32 }
33
34 pub fn lock(&self) -> RecGuard<'_, T> {
36 let guard = self.inner.lock();
37 self.holder.store(
38 crate::kernel::scheduler::core_id() as i16,
39 Ordering::Relaxed,
40 );
41 RecGuard {
42 guard: Some(guard),
43 holder: &self.holder,
44 }
45 }
46
47 pub fn try_lock(&self) -> Option<RecGuard<'_, T>> {
49 let guard = self.inner.try_lock()?;
50 self.holder.store(
51 crate::kernel::scheduler::core_id() as i16,
52 Ordering::Relaxed,
53 );
54 Some(RecGuard {
55 guard: Some(guard),
56 holder: &self.holder,
57 })
58 }
59
60 pub unsafe fn force_release_if_core(&self, cid: i16) {
67 if self.holder.load(Ordering::Relaxed) == cid {
68 self.holder.store(FREE, Ordering::Relaxed);
69 self.inner.force_unlock();
70 }
71 }
72}
73
74pub struct RecGuard<'a, T> {
75 guard: Option<MutexGuard<'a, T>>,
76 holder: &'a AtomicI16,
77}
78
79impl<'a, T> Drop for RecGuard<'a, T> {
80 fn drop(&mut self) {
81 self.holder.store(FREE, Ordering::Relaxed);
83 self.guard.take(); }
85}
86
87impl<'a, T> core::ops::Deref for RecGuard<'a, T> {
88 type Target = T;
89 fn deref(&self) -> &T {
90 self.guard.as_deref().unwrap_or_else(|| unreachable_guard())
92 }
93}
94
95impl<'a, T> core::ops::DerefMut for RecGuard<'a, T> {
96 fn deref_mut(&mut self) -> &mut T {
97 match self.guard.as_mut() {
98 Some(g) => g,
99 None => unreachable_guard(),
100 }
101 }
102}
103
104#[inline(never)]
105#[cold]
106fn unreachable_guard() -> ! {
107 loop {
109 core::hint::spin_loop();
110 }
111}
112
113pub fn selftest() -> bool {
117 static M: RecoverableMutex<u32> = RecoverableMutex::new(0);
119 let cid = crate::kernel::scheduler::core_id() as i16;
120
121 {
123 let mut g = M.lock();
124 *g = 7;
125 }
126 if M.try_lock().is_none() {
127 return false;
128 }
129
130 let g = M.lock();
132 core::mem::forget(g); if M.try_lock().is_some() {
134 return false;
135 } unsafe {
139 M.force_release_if_core(cid.wrapping_add(1));
140 }
141 if M.try_lock().is_some() {
142 return false;
143 }
144
145 unsafe {
147 M.force_release_if_core(cid);
148 }
149 M.try_lock().is_some()
150}