| 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 ] |
| 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 ] |
| 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 | )] |
| 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 ] |
| 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 ] |
| 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 ] |
| 179 | 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> { |
| 189 | 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(data:"<locked>" ) |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | let mut builder: DebugStruct<'_, '_> = f.debug_struct(name:"TryLock" ); |
| 201 | if let Some(locked: Locked<'_, T>) = self.try_lock() { |
| 202 | builder.field(name:"value" , &*locked); |
| 203 | } else { |
| 204 | builder.field(name:"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 ] |
| 227 | fn deref(&self) -> &T { |
| 228 | unsafe { &*self.lock.value.get() } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | impl<'a, T> DerefMut for Locked<'a, T> { |
| 233 | #[inline ] |
| 234 | 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 ] |
| 241 | fn drop(&mut self) { |
| 242 | self.lock.is_locked.store(val:false, self.order); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> { |
| 247 | 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 ] |
| 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 | |