1 //! A "mutex" which only supports `try_lock`
2 //!
3 //! As a futures library the eventual call to an event loop should be the only
4 //! thing that ever blocks, so this is assisted with a fast user-space
5 //! implementation of a lock that can only have a `try_lock` operation.
6 
7 use core::cell::UnsafeCell;
8 use core::ops::{Deref, DerefMut};
9 use core::sync::atomic::AtomicBool;
10 use core::sync::atomic::Ordering::SeqCst;
11 
12 /// A "mutex" around a value, similar to `std::sync::Mutex<T>`.
13 ///
14 /// This lock only supports the `try_lock` operation, however, and does not
15 /// implement poisoning.
16 #[derive(Debug)]
17 pub(crate) struct Lock<T> {
18     locked: AtomicBool,
19     data: UnsafeCell<T>,
20 }
21 
22 /// Sentinel representing an acquired lock through which the data can be
23 /// accessed.
24 pub(crate) struct TryLock<'a, T> {
25     __ptr: &'a Lock<T>,
26 }
27 
28 // The `Lock` structure is basically just a `Mutex<T>`, and these two impls are
29 // intended to mirror the standard library's corresponding impls for `Mutex<T>`.
30 //
31 // If a `T` is sendable across threads, so is the lock, and `T` must be sendable
32 // across threads to be `Sync` because it allows mutable access from multiple
33 // threads.
34 unsafe impl<T: Send> Send for Lock<T> {}
35 unsafe impl<T: Send> Sync for Lock<T> {}
36 
37 impl<T> Lock<T> {
38     /// Creates a new lock around the given value.
new(t: T) -> Self39     pub(crate) fn new(t: T) -> Self {
40         Self { locked: AtomicBool::new(false), data: UnsafeCell::new(t) }
41     }
42 
43     /// Attempts to acquire this lock, returning whether the lock was acquired or
44     /// not.
45     ///
46     /// If `Some` is returned then the data this lock protects can be accessed
47     /// through the sentinel. This sentinel allows both mutable and immutable
48     /// access.
49     ///
50     /// If `None` is returned then the lock is already locked, either elsewhere
51     /// on this thread or on another thread.
try_lock(&self) -> Option<TryLock<'_, T>>52     pub(crate) fn try_lock(&self) -> Option<TryLock<'_, T>> {
53         if !self.locked.swap(true, SeqCst) {
54             Some(TryLock { __ptr: self })
55         } else {
56             None
57         }
58     }
59 }
60 
61 impl<T> Deref for TryLock<'_, T> {
62     type Target = T;
deref(&self) -> &T63     fn deref(&self) -> &T {
64         // The existence of `TryLock` represents that we own the lock, so we
65         // can safely access the data here.
66         unsafe { &*self.__ptr.data.get() }
67     }
68 }
69 
70 impl<T> DerefMut for TryLock<'_, T> {
deref_mut(&mut self) -> &mut T71     fn deref_mut(&mut self) -> &mut T {
72         // The existence of `TryLock` represents that we own the lock, so we
73         // can safely access the data here.
74         //
75         // Additionally, we're the *only* `TryLock` in existence so mutable
76         // access should be ok.
77         unsafe { &mut *self.__ptr.data.get() }
78     }
79 }
80 
81 impl<T> Drop for TryLock<'_, T> {
drop(&mut self)82     fn drop(&mut self) {
83         self.__ptr.locked.store(false, SeqCst);
84     }
85 }
86 
87 #[cfg(test)]
88 mod tests {
89     use super::Lock;
90 
91     #[test]
smoke()92     fn smoke() {
93         let a = Lock::new(1);
94         let mut a1 = a.try_lock().unwrap();
95         assert!(a.try_lock().is_none());
96         assert_eq!(*a1, 1);
97         *a1 = 2;
98         drop(a1);
99         assert_eq!(*a.try_lock().unwrap(), 2);
100         assert_eq!(*a.try_lock().unwrap(), 2);
101     }
102 }
103