1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
use arch; use core::cell::UnsafeCell; use core::marker::Sync; use core::ops::{Drop, Deref, DerefMut}; use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; /// This type provides MUTual EXclusion based on spinning. /// /// # Description /// /// This structure behaves a lot like a normal Mutex. There are some differences: /// /// - It may be used outside the runtime. /// - A normal mutex will fail when used without the runtime, this will just lock /// - When the runtime is present, it will call the deschedule function when appropriate /// - No lock poisoning. When a fail occurs when the lock is held, no guarantees are made /// /// When calling rust functions from bare threads, such as C `pthread`s, this lock will be very /// helpful. In other cases however, you are encouraged to use the locks from the standard /// library. /// /// # Simple example /// /// ``` /// use spin; /// let spin_mutex = spin::Mutex::new(0); /// /// // Modify the data /// { /// let mut data = spin_mutex.lock(); /// *data = 2; /// } /// /// // Read the data /// let answer = /// { /// let data = spin_mutex.lock(); /// *data /// }; /// /// assert_eq!(answer, 2); /// ``` /// /// # Thread-safety example /// /// ``` /// use spin; /// use core::sync::{Arc, Barrier}; /// /// let numthreads = 1000; /// let spin_mutex = Arc::new(spin::Mutex::new(0)); /// /// // We use a barrier to ensure the readout happens after all writing /// let barrier = Arc::new(Barrier::new(numthreads + 1)); /// /// for _ in (0..numthreads) /// { /// let my_barrier = barrier.clone(); /// let my_lock = spin_mutex.clone(); /// std::thread::spawn(move|| /// { /// let mut guard = my_lock.lock(); /// *guard += 1; /// /// // Release the lock to prevent a deadlock /// drop(guard); /// my_barrier.wait(); /// }); /// } /// /// barrier.wait(); /// /// let answer = { *spin_mutex.lock() }; /// assert_eq!(answer, numthreads); /// ``` pub struct Mutex<T> { lock: AtomicUsize, data: UnsafeCell<T>, } /// A guard to which the protected data can be accessed /// /// When the guard falls out of scope it will release the lock. pub struct MutexGuard<'a, T:'a> { lock: &'a AtomicUsize, token: usize, data: &'a mut T, } unsafe impl<T> Sync for Mutex<T> {} /// A Mutex which may be used statically. /// /// ``` /// use mutex::{self, STATIC_MUTEX_INIT}; /// /// static SPLCK: spin::StaticMutex = STATIC_MUTEX_INIT; /// /// fn demo() { /// let lock = SPLCK.lock(); /// // do something with lock /// drop(lock); /// } /// ``` pub type StaticMutex = Mutex<()>; /// A initializer for StaticMutex, containing no data. pub const STATIC_MUTEX_INIT: StaticMutex = Mutex { lock: ATOMIC_USIZE_INIT, data: UnsafeCell::new(()), }; impl<T> Mutex<T> { /// Creates a new spinlock wrapping the supplied data. pub fn new(user_data: T) -> Mutex<T> { Mutex { lock: ATOMIC_USIZE_INIT, data: UnsafeCell::new(user_data), } } fn obtain_lock(&self, file_line: *const (&'static str, u32)) -> usize { let token = arch::disable_interrupts(); let null = 0; while self.lock.compare_and_swap(null, file_line as usize, Ordering::SeqCst) != null { // Do nothing } token } /// Locks the spinlock and returns a guard. /// /// The returned value may be dereferenced for data access /// and the lock will be dropped when the guard falls out of scope. /// /// ``` /// let mylock = spin::Mutex::new(0); /// { /// let mut data = mylock.lock(); /// // The lock is now locked and the data can be accessed /// *data += 1; /// // The lock is implicitly dropped /// } /// /// ``` pub fn lock(&self, file_line: &'static (&'static str, u32)) -> MutexGuard<T> { let token = self.obtain_lock(file_line); MutexGuard { lock: &self.lock, token: token, data: unsafe { &mut *self.data.get() }, } } /// Deallocates resources associated with this static mutex. /// /// This method is unsafe because it provides no guarantees that there are /// no active users of this mutex, and safety is not guaranteed if there are /// active users of this mutex. /// /// This method is required to ensure that there are no memory leaks on /// *all* platforms. It may be the case that some platforms do not leak /// memory if this method is not called, but this is not guaranteed to be /// true on all platforms. pub unsafe fn destroy(&'static self) { // nothing to do } } impl<'a, T> Deref for MutexGuard<'a, T> { type Target = T; fn deref<'b>(&'b self) -> &'b T { &*self.data } } impl<'a, T> DerefMut for MutexGuard<'a, T> { fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data } } impl<'a, T> Drop for MutexGuard<'a, T> { /// The dropping of the MutexGuard will release the lock it was created from. fn drop(&mut self) { self.lock.store(0, Ordering::SeqCst); arch::restore_interrupts(self.token); } } #[macro_export] macro_rules! lock { ($mutex:expr) => ({ static FILE_LINE: (&'static str, u32) = (file!(), line!()); $mutex.lock(&FILE_LINE) }) }