1 #![doc(html_root_url = "https://docs.rs/try-lock/0.2.2")] 2 #![deny(missing_docs)] 3 #![deny(missing_debug_implementations)] 4 #![deny(warnings)] 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 use std::cell::UnsafeCell; 47 use std::fmt; 48 use std::ops::{Deref, DerefMut}; 49 use std::sync::atomic::{AtomicBool, Ordering}; 50 51 /// A light-weight lock guarded by an atomic boolean. 52 /// 53 /// Most efficient when contention is low, acquiring the lock is a single 54 /// atomic swap, and releasing it just 1 more atomic swap. 55 /// 56 /// It is only possible to try to acquire the lock, it is not possible to 57 /// wait for the lock to become ready, like with a `Mutex`. 58 #[derive(Default)] 59 pub struct TryLock<T> { 60 is_locked: AtomicBool, 61 value: UnsafeCell<T>, 62 } 63 64 impl<T> TryLock<T> { 65 /// Create a `TryLock` around the value. 66 #[inline] new(val: T) -> TryLock<T>67 pub fn new(val: T) -> TryLock<T> { 68 TryLock { 69 is_locked: AtomicBool::new(false), 70 value: UnsafeCell::new(val), 71 } 72 } 73 74 /// Try to acquire the lock of this value. 75 /// 76 /// If the lock is already acquired by someone else, this returns 77 /// `None`. You can try to acquire again whenever you want, perhaps 78 /// by spinning a few times, or by using some other means of 79 /// notification. 80 /// 81 /// # Note 82 /// 83 /// The default memory ordering is to use `Acquire` to lock, and `Release` 84 /// to unlock. If different ordering is required, use 85 /// [`try_lock_order`](TryLock::try_lock_order). 86 #[inline] try_lock(&self) -> Option<Locked<T>>87 pub fn try_lock(&self) -> Option<Locked<T>> { 88 self.try_lock_order(Ordering::Acquire, Ordering::Release) 89 } 90 91 /// Try to acquire the lock of this value using the lock and unlock orderings. 92 /// 93 /// If the lock is already acquired by someone else, this returns 94 /// `None`. You can try to acquire again whenever you want, perhaps 95 /// by spinning a few times, or by using some other means of 96 /// notification. 97 #[inline] try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>98 pub fn try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { 99 if !self.is_locked.swap(true, lock_order) { 100 Some(Locked { 101 lock: self, 102 order: unlock_order, 103 }) 104 } else { 105 None 106 } 107 } 108 109 /// Take the value back out of the lock when this is the sole owner. 110 #[inline] into_inner(self) -> T111 pub fn into_inner(self) -> T { 112 debug_assert!(!self.is_locked.load(Ordering::Relaxed), "TryLock was mem::forgotten"); 113 // Since the compiler can statically determine this is the only owner, 114 // it's safe to take the value out. In fact, in newer versions of Rust, 115 // `UnsafeCell::into_inner` has been marked safe. 116 // 117 // To support older version (1.21), the unsafe block is still here. 118 #[allow(unused_unsafe)] 119 unsafe { 120 self.value.into_inner() 121 } 122 } 123 } 124 125 unsafe impl<T: Send> Send for TryLock<T> {} 126 unsafe impl<T: Send> Sync for TryLock<T> {} 127 128 impl<T: fmt::Debug> fmt::Debug for TryLock<T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result129 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 130 131 // Used if the TryLock cannot acquire the lock. 132 struct LockedPlaceholder; 133 134 impl fmt::Debug for LockedPlaceholder { 135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 136 f.write_str("<locked>") 137 } 138 } 139 140 let mut builder = f.debug_struct("TryLock"); 141 if let Some(locked) = self.try_lock() { 142 builder.field("value", &*locked); 143 } else { 144 builder.field("value", &LockedPlaceholder); 145 } 146 builder.finish() 147 } 148 } 149 150 /// A locked value acquired from a `TryLock`. 151 /// 152 /// The type represents an exclusive view at the underlying value. The lock is 153 /// released when this type is dropped. 154 /// 155 /// This type derefs to the underlying value. 156 #[must_use = "TryLock will immediately unlock if not used"] 157 pub struct Locked<'a, T: 'a> { 158 lock: &'a TryLock<T>, 159 order: Ordering, 160 } 161 162 impl<'a, T> Deref for Locked<'a, T> { 163 type Target = T; 164 #[inline] deref(&self) -> &T165 fn deref(&self) -> &T { 166 unsafe { &*self.lock.value.get() } 167 } 168 } 169 170 impl<'a, T> DerefMut for Locked<'a, T> { 171 #[inline] deref_mut(&mut self) -> &mut T172 fn deref_mut(&mut self) -> &mut T { 173 unsafe { &mut *self.lock.value.get() } 174 } 175 } 176 177 impl<'a, T> Drop for Locked<'a, T> { 178 #[inline] drop(&mut self)179 fn drop(&mut self) { 180 self.lock.is_locked.store(false, self.order); 181 } 182 } 183 184 impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result185 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 186 fmt::Debug::fmt(&**self, f) 187 } 188 } 189 190 #[cfg(test)] 191 mod tests { 192 use super::TryLock; 193 194 #[test] fmt_debug()195 fn fmt_debug() { 196 let lock = TryLock::new(5); 197 assert_eq!(format!("{:?}", lock), "TryLock { value: 5 }"); 198 199 let locked = lock.try_lock().unwrap(); 200 assert_eq!(format!("{:?}", locked), "5"); 201 202 assert_eq!(format!("{:?}", lock), "TryLock { value: <locked> }"); 203 } 204 } 205