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