1 #![deny(missing_docs)] 2 #![deny(missing_debug_implementations)] 3 #![deny(warnings)] 4 #![cfg_attr(not(test), no_std)] 5 6 //! A light-weight lock guarded by an atomic boolean. 7 //! 8 //! Most efficient when contention is low, acquiring the lock is a single 9 //! atomic swap, and releasing it just 1 more atomic swap. 10 //! 11 //! # Example 12 //! 13 //! ``` 14 //! use std::sync::Arc; 15 //! use try_lock::TryLock; 16 //! 17 //! // a thing we want to share 18 //! struct Widget { 19 //! name: String, 20 //! } 21 //! 22 //! // lock it up! 23 //! let widget1 = Arc::new(TryLock::new(Widget { 24 //! name: "Spanner".into(), 25 //! })); 26 //! 27 //! let widget2 = widget1.clone(); 28 //! 29 //! 30 //! // mutate the widget 31 //! let mut locked = widget1.try_lock().expect("example isn't locked yet"); 32 //! locked.name.push_str(" Bundle"); 33 //! 34 //! // hands off, buddy 35 //! let not_locked = widget2.try_lock(); 36 //! assert!(not_locked.is_none(), "widget1 has the lock"); 37 //! 38 //! // ok, you can have it 39 //! drop(locked); 40 //! 41 //! let locked2 = widget2.try_lock().expect("widget1 lock is released"); 42 //! 43 //! assert_eq!(locked2.name, "Spanner Bundle"); 44 //! ``` 45 46 #[cfg(test)] 47 extern crate core; 48 49 use core::cell::UnsafeCell; 50 use core::fmt; 51 use core::ops::{Deref, DerefMut}; 52 use core::sync::atomic::{AtomicBool, Ordering}; 53 use core::marker::PhantomData; 54 55 /// A light-weight lock guarded by an atomic boolean. 56 /// 57 /// Most efficient when contention is low, acquiring the lock is a single 58 /// atomic swap, and releasing it just 1 more atomic swap. 59 /// 60 /// It is only possible to try to acquire the lock, it is not possible to 61 /// wait for the lock to become ready, like with a `Mutex`. 62 #[derive(Default)] 63 pub struct TryLock<T> { 64 is_locked: AtomicBool, 65 value: UnsafeCell<T>, 66 } 67 68 impl<T> TryLock<T> { 69 /// Create a `TryLock` around the value. 70 #[inline] new(val: T) -> TryLock<T>71 pub const fn new(val: T) -> TryLock<T> { 72 TryLock { 73 is_locked: AtomicBool::new(false), 74 value: UnsafeCell::new(val), 75 } 76 } 77 78 /// Try to acquire the lock of this value. 79 /// 80 /// If the lock is already acquired by someone else, this returns 81 /// `None`. You can try to acquire again whenever you want, perhaps 82 /// by spinning a few times, or by using some other means of 83 /// notification. 84 /// 85 /// # Note 86 /// 87 /// The default memory ordering is to use `Acquire` to lock, and `Release` 88 /// to unlock. If different ordering is required, use 89 /// [`try_lock_explicit`](TryLock::try_lock_explicit) or 90 /// [`try_lock_explicit_unchecked`](TryLock::try_lock_explicit_unchecked). 91 #[inline] try_lock(&self) -> Option<Locked<T>>92 pub fn try_lock(&self) -> Option<Locked<T>> { 93 unsafe { 94 self.try_lock_explicit_unchecked(Ordering::Acquire, Ordering::Release) 95 } 96 } 97 98 /// Try to acquire the lock of this value using the lock and unlock orderings. 99 /// 100 /// If the lock is already acquired by someone else, this returns 101 /// `None`. You can try to acquire again whenever you want, perhaps 102 /// by spinning a few times, or by using some other means of 103 /// notification. 104 #[inline] 105 #[deprecated( 106 since = "0.2.3", 107 note = "This method is actually unsafe because it unsafely allows \ 108 the use of weaker memory ordering. Please use try_lock_explicit instead" 109 )] try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>110 pub fn try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { 111 unsafe { 112 self.try_lock_explicit_unchecked(lock_order, unlock_order) 113 } 114 } 115 116 /// Try to acquire the lock of this value using the specified lock and 117 /// unlock orderings. 118 /// 119 /// If the lock is already acquired by someone else, this returns 120 /// `None`. You can try to acquire again whenever you want, perhaps 121 /// by spinning a few times, or by using some other means of 122 /// notification. 123 /// 124 /// # Panic 125 /// 126 /// This method panics if `lock_order` is not any of `Acquire`, `AcqRel`, 127 /// and `SeqCst`, or `unlock_order` is not any of `Release` and `SeqCst`. 128 #[inline] try_lock_explicit(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>129 pub fn try_lock_explicit(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { 130 match lock_order { 131 Ordering::Acquire | 132 Ordering::AcqRel | 133 Ordering::SeqCst => {} 134 _ => panic!("lock ordering must be `Acquire`, `AcqRel`, or `SeqCst`"), 135 } 136 137 match unlock_order { 138 Ordering::Release | 139 Ordering::SeqCst => {} 140 _ => panic!("unlock ordering must be `Release` or `SeqCst`"), 141 } 142 143 unsafe { 144 self.try_lock_explicit_unchecked(lock_order, unlock_order) 145 } 146 } 147 148 /// Try to acquire the lock of this value using the specified lock and 149 /// unlock orderings without checking that the specified orderings are 150 /// strong enough to be safe. 151 /// 152 /// If the lock is already acquired by someone else, this returns 153 /// `None`. You can try to acquire again whenever you want, perhaps 154 /// by spinning a few times, or by using some other means of 155 /// notification. 156 /// 157 /// # Safety 158 /// 159 /// Unlike [`try_lock_explicit`], this method is unsafe because it does not 160 /// check that the given memory orderings are strong enough to prevent data 161 /// race. 162 /// 163 /// [`try_lock_explicit`]: Self::try_lock_explicit 164 #[inline] try_lock_explicit_unchecked(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>165 pub unsafe fn try_lock_explicit_unchecked(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { 166 if !self.is_locked.swap(true, lock_order) { 167 Some(Locked { 168 lock: self, 169 order: unlock_order, 170 _p: PhantomData, 171 }) 172 } else { 173 None 174 } 175 } 176 177 /// Take the value back out of the lock when this is the sole owner. 178 #[inline] into_inner(self) -> T179 pub fn into_inner(self) -> T { 180 debug_assert!(!self.is_locked.load(Ordering::Relaxed), "TryLock was mem::forgotten"); 181 self.value.into_inner() 182 } 183 } 184 185 unsafe impl<T: Send> Send for TryLock<T> {} 186 unsafe impl<T: Send> Sync for TryLock<T> {} 187 188 impl<T: fmt::Debug> fmt::Debug for TryLock<T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result189 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 190 191 // Used if the TryLock cannot acquire the lock. 192 struct LockedPlaceholder; 193 194 impl fmt::Debug for LockedPlaceholder { 195 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 196 f.write_str("<locked>") 197 } 198 } 199 200 let mut builder = f.debug_struct("TryLock"); 201 if let Some(locked) = self.try_lock() { 202 builder.field("value", &*locked); 203 } else { 204 builder.field("value", &LockedPlaceholder); 205 } 206 builder.finish() 207 } 208 } 209 210 /// A locked value acquired from a `TryLock`. 211 /// 212 /// The type represents an exclusive view at the underlying value. The lock is 213 /// released when this type is dropped. 214 /// 215 /// This type derefs to the underlying value. 216 #[must_use = "TryLock will immediately unlock if not used"] 217 pub struct Locked<'a, T: 'a> { 218 lock: &'a TryLock<T>, 219 order: Ordering, 220 /// Suppresses Send and Sync autotraits for `struct Locked`. 221 _p: PhantomData<*mut T>, 222 } 223 224 impl<'a, T> Deref for Locked<'a, T> { 225 type Target = T; 226 #[inline] deref(&self) -> &T227 fn deref(&self) -> &T { 228 unsafe { &*self.lock.value.get() } 229 } 230 } 231 232 impl<'a, T> DerefMut for Locked<'a, T> { 233 #[inline] deref_mut(&mut self) -> &mut T234 fn deref_mut(&mut self) -> &mut T { 235 unsafe { &mut *self.lock.value.get() } 236 } 237 } 238 239 impl<'a, T> Drop for Locked<'a, T> { 240 #[inline] drop(&mut self)241 fn drop(&mut self) { 242 self.lock.is_locked.store(false, self.order); 243 } 244 } 245 246 impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result247 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 248 fmt::Debug::fmt(&**self, f) 249 } 250 } 251 252 #[cfg(test)] 253 mod tests { 254 use super::TryLock; 255 256 #[test] fmt_debug()257 fn fmt_debug() { 258 let lock = TryLock::new(5); 259 assert_eq!(format!("{:?}", lock), "TryLock { value: 5 }"); 260 261 let locked = lock.try_lock().unwrap(); 262 assert_eq!(format!("{:?}", locked), "5"); 263 264 assert_eq!(format!("{:?}", lock), "TryLock { value: <locked> }"); 265 } 266 } 267