try_lock/
lib.rs

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("<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"]
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(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}