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