Coverage Report

Created: 2024-04-26 06:25

/rust/registry/src/index.crates.io-6f17d22bba15001f/futures-channel-0.3.28/src/lock.rs
Line
Count
Source (jump to first uncovered line)
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
0
#[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.
39
0
    pub(crate) fn new(t: T) -> Self {
40
0
        Self { locked: AtomicBool::new(false), data: UnsafeCell::new(t) }
41
0
    }
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.
52
0
    pub(crate) fn try_lock(&self) -> Option<TryLock<'_, T>> {
53
0
        if !self.locked.swap(true, SeqCst) {
54
0
            Some(TryLock { __ptr: self })
55
        } else {
56
0
            None
57
        }
58
0
    }
59
}
60
61
impl<T> Deref for TryLock<'_, T> {
62
    type Target = T;
63
0
    fn deref(&self) -> &T {
64
0
        // The existence of `TryLock` represents that we own the lock, so we
65
0
        // can safely access the data here.
66
0
        unsafe { &*self.__ptr.data.get() }
67
0
    }
68
}
69
70
impl<T> DerefMut for TryLock<'_, T> {
71
0
    fn deref_mut(&mut self) -> &mut T {
72
0
        // The existence of `TryLock` represents that we own the lock, so we
73
0
        // can safely access the data here.
74
0
        //
75
0
        // Additionally, we're the *only* `TryLock` in existence so mutable
76
0
        // access should be ok.
77
0
        unsafe { &mut *self.__ptr.data.get() }
78
0
    }
79
}
80
81
impl<T> Drop for TryLock<'_, T> {
82
0
    fn drop(&mut self) {
83
0
        self.__ptr.locked.store(false, SeqCst);
84
0
    }
85
}
86
87
#[cfg(test)]
88
mod tests {
89
    use super::Lock;
90
91
    #[test]
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
}