1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 #![allow(clippy::undocumented_unsafe_blocks)] 4 #![cfg_attr(feature = "alloc", feature(allocator_api))] 5 #![allow(clippy::missing_safety_doc)] 6 7 use core::{ 8 cell::{Cell, UnsafeCell}, 9 marker::PhantomPinned, 10 ops::{Deref, DerefMut}, 11 pin::Pin, 12 sync::atomic::{AtomicBool, Ordering}, 13 }; 14 #[cfg(feature = "std")] 15 use std::{ 16 sync::Arc, 17 thread::{self, sleep, Builder, Thread}, 18 time::Duration, 19 }; 20 21 use pin_init::*; 22 #[allow(unused_attributes)] 23 #[path = "./linked_list.rs"] 24 pub mod linked_list; 25 use linked_list::*; 26 27 pub struct SpinLock { 28 inner: AtomicBool, 29 } 30 31 impl SpinLock { 32 #[inline] 33 pub fn acquire(&self) -> SpinLockGuard<'_> { 34 while self 35 .inner 36 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) 37 .is_err() 38 { 39 #[cfg(feature = "std")] 40 while self.inner.load(Ordering::Relaxed) { 41 thread::yield_now(); 42 } 43 } 44 SpinLockGuard(self) 45 } 46 47 #[inline] 48 #[allow(clippy::new_without_default)] 49 pub const fn new() -> Self { 50 Self { 51 inner: AtomicBool::new(false), 52 } 53 } 54 } 55 56 pub struct SpinLockGuard<'a>(&'a SpinLock); 57 58 impl Drop for SpinLockGuard<'_> { 59 #[inline] 60 fn drop(&mut self) { 61 self.0.inner.store(false, Ordering::Release); 62 } 63 } 64 65 #[pin_data] 66 pub struct CMutex<T> { 67 #[pin] 68 wait_list: ListHead, 69 spin_lock: SpinLock, 70 locked: Cell<bool>, 71 #[pin] 72 data: UnsafeCell<T>, 73 } 74 75 impl<T> CMutex<T> { 76 #[inline] 77 pub fn new(val: impl PinInit<T>) -> impl PinInit<Self> { 78 pin_init!(CMutex { 79 wait_list <- ListHead::new(), 80 spin_lock: SpinLock::new(), 81 locked: Cell::new(false), 82 data <- UnsafeCell::pin_init(val), 83 }) 84 } 85 86 #[inline] 87 pub fn lock(&self) -> Pin<CMutexGuard<'_, T>> { 88 let mut sguard = self.spin_lock.acquire(); 89 if self.locked.get() { 90 stack_pin_init!(let _wait_entry = WaitEntry::insert_new(&self.wait_list)); 91 // println!("wait list length: {}", self.wait_list.size()); 92 while self.locked.get() { 93 drop(sguard); 94 #[cfg(feature = "std")] 95 thread::park(); 96 sguard = self.spin_lock.acquire(); 97 } 98 } 99 self.locked.set(true); 100 unsafe { 101 Pin::new_unchecked(CMutexGuard { 102 mtx: self, 103 _pin: PhantomPinned, 104 }) 105 } 106 } 107 108 #[allow(dead_code)] 109 pub fn get_data_mut(self: Pin<&mut Self>) -> &mut T { 110 // SAFETY: we have an exclusive reference and thus nobody has access to data. 111 unsafe { &mut *self.data.get() } 112 } 113 } 114 115 unsafe impl<T: Send> Send for CMutex<T> {} 116 unsafe impl<T: Send> Sync for CMutex<T> {} 117 118 pub struct CMutexGuard<'a, T> { 119 mtx: &'a CMutex<T>, 120 _pin: PhantomPinned, 121 } 122 123 impl<T> Drop for CMutexGuard<'_, T> { 124 #[inline] 125 fn drop(&mut self) { 126 let sguard = self.mtx.spin_lock.acquire(); 127 self.mtx.locked.set(false); 128 if let Some(list_field) = self.mtx.wait_list.next() { 129 let _wait_entry = list_field.as_ptr().cast::<WaitEntry>(); 130 #[cfg(feature = "std")] 131 unsafe { 132 (*_wait_entry).thread.unpark() 133 }; 134 } 135 drop(sguard); 136 } 137 } 138 139 impl<T> Deref for CMutexGuard<'_, T> { 140 type Target = T; 141 142 #[inline] 143 fn deref(&self) -> &Self::Target { 144 unsafe { &*self.mtx.data.get() } 145 } 146 } 147 148 impl<T> DerefMut for CMutexGuard<'_, T> { 149 #[inline] 150 fn deref_mut(&mut self) -> &mut Self::Target { 151 unsafe { &mut *self.mtx.data.get() } 152 } 153 } 154 155 #[pin_data] 156 #[repr(C)] 157 struct WaitEntry { 158 #[pin] 159 wait_list: ListHead, 160 #[cfg(feature = "std")] 161 thread: Thread, 162 } 163 164 impl WaitEntry { 165 #[inline] 166 fn insert_new(list: &ListHead) -> impl PinInit<Self> + '_ { 167 #[cfg(feature = "std")] 168 { 169 pin_init!(Self { 170 thread: thread::current(), 171 wait_list <- ListHead::insert_prev(list), 172 }) 173 } 174 #[cfg(not(feature = "std"))] 175 { 176 pin_init!(Self { 177 wait_list <- ListHead::insert_prev(list), 178 }) 179 } 180 } 181 } 182 183 #[cfg_attr(test, test)] 184 #[allow(dead_code)] 185 fn main() { 186 #[cfg(feature = "std")] 187 { 188 let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap(); 189 let mut handles = vec![]; 190 let thread_count = 20; 191 let workload = if cfg!(miri) { 100 } else { 1_000 }; 192 for i in 0..thread_count { 193 let mtx = mtx.clone(); 194 handles.push( 195 Builder::new() 196 .name(format!("worker #{i}")) 197 .spawn(move || { 198 for _ in 0..workload { 199 *mtx.lock() += 1; 200 } 201 println!("{i} halfway"); 202 sleep(Duration::from_millis((i as u64) * 10)); 203 for _ in 0..workload { 204 *mtx.lock() += 1; 205 } 206 println!("{i} finished"); 207 }) 208 .expect("should not fail"), 209 ); 210 } 211 for h in handles { 212 h.join().expect("thread panicked"); 213 } 214 println!("{:?}", *mtx.lock()); 215 assert_eq!(*mtx.lock(), workload * thread_count * 2); 216 } 217 } 218