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 | |