fused_lock/
lib.rs

1use std::{
2    cell::UnsafeCell,
3    ops::{Deref, DerefMut},
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7///
8/// A special RwLock which can be locked exclusively any number of consecutive times,
9///  but once initially locked shared, can never be unlocked.
10/// This allows unguarded reads to occur
11pub struct FusedRwLock<T: ?Sized> {
12    inner: parking_lot::RwLock<()>,
13    locked: AtomicBool,
14    object: UnsafeCell<T>,
15}
16
17unsafe impl<T: ?Sized + Send> Send for FusedRwLock<T> {}
18unsafe impl<T: ?Sized + Send + Sync> Sync for FusedRwLock<T> {}
19
20impl<T: Default> Default for FusedRwLock<T> {
21    fn default() -> Self {
22        Self::new(Default::default())
23    }
24}
25
26impl<T> FusedRwLock<T> {
27    ///
28    /// Constructs a new, initially unlocked, RwLock
29    pub const fn new(x: T) -> Self {
30        Self {
31            inner: parking_lot::const_rwlock(()),
32            locked: AtomicBool::new(false),
33            object: UnsafeCell::new(x),
34        }
35    }
36
37    ///
38    /// Moves the inner value out of the FusedRwLock.
39    /// This is sound because self is moved into the function, and thus no other accesses exist
40    pub fn into_inner(self) -> T {
41        self.object.into_inner()
42    }
43}
44
45impl<T: ?Sized> FusedRwLock<T> {
46    ///
47    /// Mutably borrows the interior of the lock, if it has not been locked for reading access
48    /// This is sound because taking self by &mut statically guarantees no other accesses exist.
49    /// Returns None if the lock has been locked for reading
50    pub fn try_get_mut(&mut self) -> Option<&mut T> {
51        if *self.locked.get_mut() {
52            Some(self.object.get_mut())
53        } else {
54            None
55        }
56    }
57
58    ///
59    /// Mutably borrows the interior of the lock, even if it has been locked for reading.
60    /// This function is unsafe because, while not necessarily undefined behaviour, calling this function
61    ///  after it was locked for reading can be used to violate the logical invariant of FusedRwLock.
62    pub unsafe fn get_mut_unlocked(&mut self) -> &mut T {
63        self.object.get_mut()
64    }
65
66    ///
67    /// Check if the FusedRwLock has been locked for reading.
68    /// This does not guarantee any synchronization, even if it returns true. Except where self is reborrowed from &mut,
69    ///  it should only be used as a hint to avoid needless calls to self.try_read
70    /// A return of true is guaranteed to remain true for the lifetime of the lock.
71    /// A return of false may be invalidated at any time.
72    pub fn is_locked(&self) -> bool {
73        self.locked.load(Ordering::Relaxed)
74    }
75
76    ///
77    /// Locks this FusedRwLock for reading.
78    /// After this call, it becomes impossible to acquire the lock for writing,
79    ///  and safe code cannot be used to modify the inner value (except inside an UnsafeCell)
80    pub fn lock(&self) {
81        let _guard = self.inner.read();
82        self.locked
83            .store(true, std::sync::atomic::Ordering::Release)
84    }
85
86    ///
87    /// Returns a shared reference to the interior of the lock, if it has been locked for reading.
88    pub fn try_read(&self) -> Option<&T> {
89        if self.locked.load(Ordering::Acquire) {
90            // Safety:
91            // Because self.locked is set, the lock can never be borrowed exclusively again
92            Some(unsafe { &*self.object.get() })
93        } else {
94            None
95        }
96    }
97
98    ///
99    /// Locks the RwLock for reading, and returns a shared reference to the interior of the lock
100    pub fn read(&self) -> &T {
101        if !self.is_locked() {
102            self.lock();
103        }
104        self.try_read().unwrap()
105    }
106
107    ///
108    /// Acquires an exclusive lock to the interior of the lock, if this lock has not already been locked for reading.
109    /// Otherwise, returns None
110    pub fn try_write(&self) -> Option<FusedRwLockGuard<T>> {
111        // Optimization, since a true return from self.is_locked is guaranteed to continue forever
112        if !self.is_locked() {
113            let guard = self.inner.write();
114            if !self.is_locked() {
115                Some(FusedRwLockGuard {
116                    _guard: guard,
117                    // Safety:
118                    // Because self.locked is not set, there are no readers. Other writers are excluded by the fact that the WriteGuard is held.
119                    inner: unsafe { &mut *self.object.get() },
120                })
121            } else {
122                None
123            }
124        } else {
125            None
126        }
127    }
128}
129
130///
131/// An RAII guard that holds exclusive mutable access to the inner object.
132pub struct FusedRwLockGuard<'a, T: ?Sized> {
133    inner: &'a mut T,
134    _guard: parking_lot::RwLockWriteGuard<'a, ()>,
135}
136
137impl<'a, T: ?Sized> Deref for FusedRwLockGuard<'a, T> {
138    type Target = T;
139    fn deref(&self) -> &T {
140        // SAFETY:
141        // self.guard ensures that self.cell can be borrowed
142        self.inner
143    }
144}
145
146impl<'a, T: ?Sized> DerefMut for FusedRwLockGuard<'a, T> {
147    fn deref_mut(&mut self) -> &mut T {
148        // SAFETY:
149        // self.guard ensures that self.cell can be borrowed
150        self.inner
151    }
152}