• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]
new(val: T) -> TryLock<T>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]
try_lock(&self) -> Option<Locked<T>>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     )]
try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>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]
try_lock_explicit(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>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]
try_lock_explicit_unchecked(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>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]
into_inner(self) -> T179     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> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result189     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("<locked>")
197             }
198         }
199 
200         let mut builder = f.debug_struct("TryLock");
201         if let Some(locked) = self.try_lock() {
202             builder.field("value", &*locked);
203         } else {
204             builder.field("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]
deref(&self) -> &T227     fn deref(&self) -> &T {
228         unsafe { &*self.lock.value.get() }
229     }
230 }
231 
232 impl<'a, T> DerefMut for Locked<'a, T> {
233     #[inline]
deref_mut(&mut self) -> &mut T234     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]
drop(&mut self)241     fn drop(&mut self) {
242         self.lock.is_locked.store(false, self.order);
243     }
244 }
245 
246 impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result247     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]
fmt_debug()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