1 #![doc(html_root_url = "https://docs.rs/try-lock/0.2.2")]
2 #![deny(missing_docs)]
3 #![deny(missing_debug_implementations)]
4 #![deny(warnings)]
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 use std::cell::UnsafeCell;
47 use std::fmt;
48 use std::ops::{Deref, DerefMut};
49 use std::sync::atomic::{AtomicBool, Ordering};
50 
51 /// A light-weight lock guarded by an atomic boolean.
52 ///
53 /// Most efficient when contention is low, acquiring the lock is a single
54 /// atomic swap, and releasing it just 1 more atomic swap.
55 ///
56 /// It is only possible to try to acquire the lock, it is not possible to
57 /// wait for the lock to become ready, like with a `Mutex`.
58 #[derive(Default)]
59 pub struct TryLock<T> {
60     is_locked: AtomicBool,
61     value: UnsafeCell<T>,
62 }
63 
64 impl<T> TryLock<T> {
65     /// Create a `TryLock` around the value.
66     #[inline]
new(val: T) -> TryLock<T>67     pub fn new(val: T) -> TryLock<T> {
68         TryLock {
69             is_locked: AtomicBool::new(false),
70             value: UnsafeCell::new(val),
71         }
72     }
73 
74     /// Try to acquire the lock of this value.
75     ///
76     /// If the lock is already acquired by someone else, this returns
77     /// `None`. You can try to acquire again whenever you want, perhaps
78     /// by spinning a few times, or by using some other means of
79     /// notification.
80     ///
81     /// # Note
82     ///
83     /// The default memory ordering is to use `Acquire` to lock, and `Release`
84     /// to unlock. If different ordering is required, use
85     /// [`try_lock_order`](TryLock::try_lock_order).
86     #[inline]
try_lock(&self) -> Option<Locked<T>>87     pub fn try_lock(&self) -> Option<Locked<T>> {
88         self.try_lock_order(Ordering::Acquire, Ordering::Release)
89     }
90 
91     /// Try to acquire the lock of this value using the lock and unlock orderings.
92     ///
93     /// If the lock is already acquired by someone else, this returns
94     /// `None`. You can try to acquire again whenever you want, perhaps
95     /// by spinning a few times, or by using some other means of
96     /// notification.
97     #[inline]
try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>>98     pub fn try_lock_order(&self, lock_order: Ordering, unlock_order: Ordering) -> Option<Locked<T>> {
99         if !self.is_locked.swap(true, lock_order) {
100             Some(Locked {
101                 lock: self,
102                 order: unlock_order,
103             })
104         } else {
105             None
106         }
107     }
108 
109     /// Take the value back out of the lock when this is the sole owner.
110     #[inline]
into_inner(self) -> T111     pub fn into_inner(self) -> T {
112         debug_assert!(!self.is_locked.load(Ordering::Relaxed), "TryLock was mem::forgotten");
113         // Since the compiler can statically determine this is the only owner,
114         // it's safe to take the value out. In fact, in newer versions of Rust,
115         // `UnsafeCell::into_inner` has been marked safe.
116         //
117         // To support older version (1.21), the unsafe block is still here.
118         #[allow(unused_unsafe)]
119         unsafe {
120             self.value.into_inner()
121         }
122     }
123 }
124 
125 unsafe impl<T: Send> Send for TryLock<T> {}
126 unsafe impl<T: Send> Sync for TryLock<T> {}
127 
128 impl<T: fmt::Debug> fmt::Debug for TryLock<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result129     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130 
131         // Used if the TryLock cannot acquire the lock.
132         struct LockedPlaceholder;
133 
134         impl fmt::Debug for LockedPlaceholder {
135             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136                 f.write_str("<locked>")
137             }
138         }
139 
140         let mut builder = f.debug_struct("TryLock");
141         if let Some(locked) = self.try_lock() {
142             builder.field("value", &*locked);
143         } else {
144             builder.field("value", &LockedPlaceholder);
145         }
146         builder.finish()
147     }
148 }
149 
150 /// A locked value acquired from a `TryLock`.
151 ///
152 /// The type represents an exclusive view at the underlying value. The lock is
153 /// released when this type is dropped.
154 ///
155 /// This type derefs to the underlying value.
156 #[must_use = "TryLock will immediately unlock if not used"]
157 pub struct Locked<'a, T: 'a> {
158     lock: &'a TryLock<T>,
159     order: Ordering,
160 }
161 
162 impl<'a, T> Deref for Locked<'a, T> {
163     type Target = T;
164     #[inline]
deref(&self) -> &T165     fn deref(&self) -> &T {
166         unsafe { &*self.lock.value.get() }
167     }
168 }
169 
170 impl<'a, T> DerefMut for Locked<'a, T> {
171     #[inline]
deref_mut(&mut self) -> &mut T172     fn deref_mut(&mut self) -> &mut T {
173         unsafe { &mut *self.lock.value.get() }
174     }
175 }
176 
177 impl<'a, T> Drop for Locked<'a, T> {
178     #[inline]
drop(&mut self)179     fn drop(&mut self) {
180         self.lock.is_locked.store(false, self.order);
181     }
182 }
183 
184 impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result185     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186         fmt::Debug::fmt(&**self, f)
187     }
188 }
189 
190 #[cfg(test)]
191 mod tests {
192     use super::TryLock;
193 
194     #[test]
fmt_debug()195     fn fmt_debug() {
196         let lock = TryLock::new(5);
197         assert_eq!(format!("{:?}", lock), "TryLock { value: 5 }");
198 
199         let locked = lock.try_lock().unwrap();
200         assert_eq!(format!("{:?}", locked), "5");
201 
202         assert_eq!(format!("{:?}", lock), "TryLock { value: <locked> }");
203     }
204 }
205