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}