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)]
48extern crate core;
49
50use core::cell::UnsafeCell;
51use core::fmt;
52use core::ops::{Deref, DerefMut};
53use core::sync::atomic::{AtomicBool, Ordering};
54use 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)]
64pub struct TryLock<T> {
65 is_locked: AtomicBool,
66 value: UnsafeCell<T>,
67}
68
69impl<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
194unsafe impl<T: Send> Send for TryLock<T> {}
195unsafe impl<T: Send> Sync for TryLock<T> {}
196
197impl<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"]
226pub 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
233impl<'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
241impl<'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
248impl<'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
255impl<'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)]
262mod 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