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)]
47extern crate core;
48
49use core::cell::UnsafeCell;
50use core::fmt;
51use core::ops::{Deref, DerefMut};
52use core::sync::atomic::{AtomicBool, Ordering};
53use 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)]
63pub struct TryLock<T> {
64 is_locked: AtomicBool,
65 value: UnsafeCell<T>,
66}
67
68impl<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
185unsafe impl<T: Send> Send for TryLock<T> {}
186unsafe impl<T: Send> Sync for TryLock<T> {}
187
188impl<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"]
217pub 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
224impl<'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
232impl<'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
239impl<'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
246impl<'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)]
253mod 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