try_lock/
lib.rs

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
46use std::cell::UnsafeCell;
47use std::fmt;
48use std::ops::{Deref, DerefMut};
49use 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)]
59pub struct TryLock<T> {
60    is_locked: AtomicBool,
61    value: UnsafeCell<T>,
62}
63
64impl<T> TryLock<T> {
65    /// Create a `TryLock` around the value.
66    #[inline]
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]
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]
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]
111    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
125unsafe impl<T: Send> Send for TryLock<T> {}
126unsafe impl<T: Send> Sync for TryLock<T> {}
127
128impl<T: fmt::Debug> fmt::Debug for TryLock<T> {
129    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"]
157pub struct Locked<'a, T: 'a> {
158    lock: &'a TryLock<T>,
159    order: Ordering,
160}
161
162impl<'a, T> Deref for Locked<'a, T> {
163    type Target = T;
164    #[inline]
165    fn deref(&self) -> &T {
166        unsafe { &*self.lock.value.get() }
167    }
168}
169
170impl<'a, T> DerefMut for Locked<'a, T> {
171    #[inline]
172    fn deref_mut(&mut self) -> &mut T {
173        unsafe { &mut *self.lock.value.get() }
174    }
175}
176
177impl<'a, T> Drop for Locked<'a, T> {
178    #[inline]
179    fn drop(&mut self) {
180        self.lock.is_locked.store(false, self.order);
181    }
182}
183
184impl<'a, T: fmt::Debug> fmt::Debug for Locked<'a, T> {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        fmt::Debug::fmt(&**self, f)
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::TryLock;
193
194    #[test]
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}