1 | #![doc (html_root_url = "https://docs.rs/try-lock/0.2.3" )] |
2 | #![deny (missing_docs)] |
3 | #![deny (missing_debug_implementations)] |
4 | #![deny (warnings)] |
5 | #![cfg_attr (not(test), 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 | #[cfg (test)] |
48 | extern crate core; |
49 | |
50 | use core::cell::UnsafeCell; |
51 | use core::fmt; |
52 | use core::ops::{Deref, DerefMut}; |
53 | use core::sync::atomic::{AtomicBool, Ordering}; |
54 | use core::marker::PhantomData; |
55 | |
56 | /// A light-weight lock guarded by an atomic boolean. |
57 | /// |
58 | /// Most efficient when contention is low, acquiring the lock is a single |
59 | /// atomic swap, and releasing it just 1 more atomic swap. |
60 | /// |
61 | /// It is only possible to try to acquire the lock, it is not possible to |
62 | /// wait for the lock to become ready, like with a `Mutex`. |
63 | #[derive (Default)] |
64 | pub struct TryLock<T> { |
65 | is_locked: AtomicBool, |
66 | value: UnsafeCell<T>, |
67 | } |
68 | |
69 | impl<T> TryLock<T> { |
70 | /// Create a `TryLock` around the value. |
71 | #[inline ] |
72 | pub fn new(val: T) -> TryLock<T> { |
73 | TryLock { |
74 | is_locked: AtomicBool::new(false), |
75 | value: UnsafeCell::new(val), |
76 | } |
77 | } |
78 | |
79 | /// Try to acquire the lock of this value. |
80 | /// |
81 | /// If the lock is already acquired by someone else, this returns |
82 | /// `None`. You can try to acquire again whenever you want, perhaps |
83 | /// by spinning a few times, or by using some other means of |
84 | /// notification. |
85 | /// |
86 | /// # Note |
87 | /// |
88 | /// The default memory ordering is to use `Acquire` to lock, and `Release` |
89 | /// to unlock. If different ordering is required, use |
90 | /// [`try_lock_explicit`](TryLock::try_lock_explicit) or |
91 | /// [`try_lock_explicit_unchecked`](TryLock::try_lock_explicit_unchecked). |
92 | #[inline ] |
93 | pub fn try_lock(&self) -> Option<Locked<T>> { |
94 | unsafe { |
95 | self.try_lock_explicit_unchecked(Ordering::Acquire, Ordering::Release) |
96 | } |
97 | } |
98 | |
99 | /// Try to acquire the lock of this value using the lock and unlock orderings. |
100 | /// |
101 | /// If the lock is already acquired by someone else, this returns |
102 | /// `None`. You can try to acquire again whenever you want, perhaps |
103 | /// by spinning a few times, or by using some other means of |
104 | /// notification. |
105 | #[inline ] |
106 | #[deprecated ( |
107 | since = "0.2.3" , |
108 | note = "This method is actually unsafe because it unsafely allows \ |
109 | the use of weaker memory ordering. Please use try_lock_explicit instead" |
110 | )] |
111 | pub fn try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { |
112 | unsafe { |
113 | self.try_lock_explicit_unchecked(lock_order, unlock_order) |
114 | } |
115 | } |
116 | |
117 | /// Try to acquire the lock of this value using the specified lock and |
118 | /// unlock orderings. |
119 | /// |
120 | /// If the lock is already acquired by someone else, this returns |
121 | /// `None`. You can try to acquire again whenever you want, perhaps |
122 | /// by spinning a few times, or by using some other means of |
123 | /// notification. |
124 | /// |
125 | /// # Panic |
126 | /// |
127 | /// This method panics if `lock_order` is not any of `Acquire`, `AcqRel`, |
128 | /// and `SeqCst`, or `unlock_order` is not any of `Release` and `SeqCst`. |
129 | #[inline ] |
130 | pub fn try_lock_explicit(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { |
131 | match lock_order { |
132 | Ordering::Acquire | |
133 | Ordering::AcqRel | |
134 | Ordering::SeqCst => {} |
135 | _ => panic!("lock ordering must be `Acquire`, `AcqRel`, or `SeqCst`" ), |
136 | } |
137 | |
138 | match unlock_order { |
139 | Ordering::Release | |
140 | Ordering::SeqCst => {} |
141 | _ => panic!("unlock ordering must be `Release` or `SeqCst`" ), |
142 | } |
143 | |
144 | unsafe { |
145 | self.try_lock_explicit_unchecked(lock_order, unlock_order) |
146 | } |
147 | } |
148 | |
149 | /// Try to acquire the lock of this value using the specified lock and |
150 | /// unlock orderings without checking that the specified orderings are |
151 | /// strong enough to be safe. |
152 | /// |
153 | /// If the lock is already acquired by someone else, this returns |
154 | /// `None`. You can try to acquire again whenever you want, perhaps |
155 | /// by spinning a few times, or by using some other means of |
156 | /// notification. |
157 | /// |
158 | /// # Safety |
159 | /// |
160 | /// Unlike [`try_lock_explicit`], this method is unsafe because it does not |
161 | /// check that the given memory orderings are strong enough to prevent data |
162 | /// race. |
163 | /// |
164 | /// [`try_lock_explicit`]: Self::try_lock_explicit |
165 | #[inline ] |
166 | pub unsafe fn try_lock_explicit_unchecked(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> { |
167 | if !self.is_locked.swap(true, lock_order) { |
168 | Some(Locked { |
169 | lock: self, |
170 | order: unlock_order, |
171 | _p: PhantomData, |
172 | }) |
173 | } else { |
174 | None |
175 | } |
176 | } |
177 | |
178 | /// Take the value back out of the lock when this is the sole owner. |
179 | #[inline ] |
180 | pub fn into_inner(self) -> T { |
181 | debug_assert!(!self.is_locked.load(Ordering::Relaxed), "TryLock was mem::forgotten" ); |
182 | // Since the compiler can statically determine this is the only owner, |
183 | // it's safe to take the value out. In fact, in newer versions of Rust, |
184 | // `UnsafeCell::into_inner` has been marked safe. |
185 | // |
186 | // To support older version (1.21), the unsafe block is still here. |
187 | #[allow (unused_unsafe)] |
188 | unsafe { |
189 | self.value.into_inner() |
190 | } |
191 | } |
192 | } |
193 | |
194 | unsafe impl<T: Send> Send for TryLock<T> {} |
195 | unsafe impl<T: Send> Sync for TryLock<T> {} |
196 | |
197 | impl<T: fmt::Debug> fmt::Debug for TryLock<T> { |
198 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
199 | |
200 | // Used if the TryLock cannot acquire the lock. |
201 | struct LockedPlaceholder; |
202 | |
203 | impl fmt::Debug for LockedPlaceholder { |
204 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
205 | f.write_str(data:"<locked>" ) |
206 | } |
207 | } |
208 | |
209 | let mut builder: DebugStruct<'_, '_> = f.debug_struct(name:"TryLock" ); |
210 | if let Some(locked: Locked<'_, T>) = self.try_lock() { |
211 | builder.field(name:"value" , &*locked); |
212 | } else { |
213 | builder.field(name:"value" , &LockedPlaceholder); |
214 | } |
215 | builder.finish() |
216 | } |
217 | } |
218 | |
219 | /// A locked value acquired from a `TryLock`. |
220 | /// |
221 | /// The type represents an exclusive view at the underlying value. The lock is |
222 | /// released when this type is dropped. |
223 | /// |
224 | /// This type derefs to the underlying value. |
225 | #[must_use = "TryLock will immediately unlock if not used" ] |
226 | pub struct Locked<'a, T: 'a> { |
227 | lock: &'a TryLock<T>, |
228 | order: Ordering, |
229 | /// Suppresses Send and Sync autotraits for `struct Locked`. |
230 | _p: PhantomData<*mut T>, |
231 | } |
232 | |
233 | impl<'a, T> Deref for Locked<'a, T> { |
234 | type Target = T; |
235 | #[inline ] |
236 | fn deref(&self) -> &T { |
237 | unsafe { &*self.lock.value.get() } |
238 | } |
239 | } |
240 | |
241 | impl<'a, T> DerefMut for Locked<'a, T> { |
242 | #[inline ] |
243 | fn deref_mut(&mut self) -> &mut T { |
244 | unsafe { &mut *self.lock.value.get() } |
245 | } |
246 | } |
247 | |
248 | impl<'a, T> Drop for Locked<'a, T> { |
249 | #[inline ] |
250 | fn drop(&mut self) { |
251 | self.lock.is_locked.store(val:false, self.order); |
252 | } |
253 | } |
254 | |
255 | impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> { |
256 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
257 | fmt::Debug::fmt(&**self, f) |
258 | } |
259 | } |
260 | |
261 | #[cfg (test)] |
262 | mod tests { |
263 | use super::TryLock; |
264 | |
265 | #[test ] |
266 | fn fmt_debug() { |
267 | let lock = TryLock::new(5); |
268 | assert_eq!(format!("{:?}" , lock), "TryLock { value: 5 }" ); |
269 | |
270 | let locked = lock.try_lock().unwrap(); |
271 | assert_eq!(format!("{:?}" , locked), "5" ); |
272 | |
273 | assert_eq!(format!("{:?}" , lock), "TryLock { value: <locked> }" ); |
274 | } |
275 | } |
276 | |