Skip to main content

quick_cache/
rw_lock.rs

1use std::ops::{Deref, DerefMut};
2
3#[cfg(feature = "parking_lot")]
4type InnerRwLock<T> = parking_lot::RwLock<T>;
5#[cfg(all(not(feature = "parking_lot"), feature = "sharded-lock"))]
6type InnerRwLock<T> = crossbeam_utils::sync::ShardedLock<T>;
7#[cfg(all(not(feature = "parking_lot"), not(feature = "sharded-lock")))]
8type InnerRwLock<T> = std::sync::RwLock<T>;
9
10#[cfg(feature = "parking_lot")]
11type InnerRwLockReadGuard<'rwlock, T> = parking_lot::RwLockReadGuard<'rwlock, T>;
12#[cfg(all(not(feature = "parking_lot"), feature = "sharded-lock"))]
13type InnerRwLockReadGuard<'rwlock, T> = crossbeam_utils::sync::ShardedLockReadGuard<'rwlock, T>;
14#[cfg(all(not(feature = "parking_lot"), not(feature = "sharded-lock")))]
15type InnerRwLockReadGuard<'rwlock, T> = std::sync::RwLockReadGuard<'rwlock, T>;
16
17#[cfg(feature = "parking_lot")]
18type InnerRwLockWriteGuard<'rwlock, T> = parking_lot::RwLockWriteGuard<'rwlock, T>;
19#[cfg(all(not(feature = "parking_lot"), feature = "sharded-lock"))]
20type InnerRwLockWriteGuard<'rwlock, T> = crossbeam_utils::sync::ShardedLockWriteGuard<'rwlock, T>;
21#[cfg(all(not(feature = "parking_lot"), not(feature = "sharded-lock")))]
22type InnerRwLockWriteGuard<'rwlock, T> = std::sync::RwLockWriteGuard<'rwlock, T>;
23
24/// A reader-writer lock.
25///
26/// This type of lock allows a number of readers or at most one writer at any
27/// point in time. The write portion of this lock typically allows modification
28/// of the underlying data (exclusive access) and the read portion of this lock
29/// typically allows for read-only access (shared access).
30///
31/// In comparison, a [`Mutex`] does not distinguish between readers or writers
32/// that acquire the lock, therefore blocking any threads waiting for the lock to
33/// become available. An `RwLock` will allow any number of readers to acquire the
34/// lock as long as a writer is not holding the lock.
35///
36/// The type parameter `T` represents the data that this lock protects. It is
37/// required that `T` satisfies [`Send`] to be shared across threads and
38/// [`Sync`] to allow concurrent access through readers. The RAII guards
39/// returned from the locking methods implement [`Deref`] (and [`DerefMut`]
40/// for the `write` methods) to allow access to the content of the lock.
41///
42/// # Poisoning
43///
44/// An `RwLock` might become poisoned on a panic. Note, however, that an `RwLock`
45/// may only be poisoned if a panic occurs while it is locked exclusively (write
46/// mode). If a panic occurs in any reader, then the lock will not be poisoned.
47#[derive(Default, Debug)]
48#[repr(transparent)]
49pub struct RwLock<T: ?Sized>(InnerRwLock<T>);
50
51/// RAII structure used to release the shared read access of a lock when dropped.
52#[repr(transparent)]
53#[must_use = "if unused the RwLock will immediately unlock"]
54pub struct RwLockReadGuard<'rwlock, T: ?Sized>(InnerRwLockReadGuard<'rwlock, T>);
55
56/// RAII structure used to release the exclusive write access of a lock when dropped.
57#[repr(transparent)]
58#[must_use = "if unused the RwLock will immediately unlock"]
59pub struct RwLockWriteGuard<'rwlock, T: ?Sized>(InnerRwLockWriteGuard<'rwlock, T>);
60
61#[cfg(not(feature = "sharded-lock"))]
62impl<T> RwLock<T> {
63    /// Creates a new instance of an `RwLock<T>` which is unlocked.
64    pub const fn new(t: T) -> Self {
65        Self(InnerRwLock::new(t))
66    }
67}
68
69#[cfg(feature = "sharded-lock")]
70impl<T> RwLock<T> {
71    /// Creates a new instance of an `RwLock<T>` which is unlocked.
72    pub fn new(t: T) -> Self {
73        Self(InnerRwLock::new(t))
74    }
75}
76
77impl<T: ?Sized> RwLock<T> {
78    /// Locks this `RwLock` with shared read access, blocking the current thread
79    /// until it can be acquired.
80    ///
81    /// The calling thread will be blocked until there are no more writers which
82    /// hold the lock. There may be other readers currently inside the lock when
83    /// this method returns. This method does not provide any guarantees with
84    /// respect to the ordering of whether contentious readers or writers will
85    /// acquire the lock first.
86    ///
87    /// Returns an RAII guard which will release this thread's shared access
88    /// once it is dropped.
89    ///
90    /// # Panics
91    ///
92    /// This function might panic when called if the lock is already held by the
93    /// current thread, or if the `RwLock` is poisoned. An `RwLock` might be
94    /// poisoned whenever a writer panics while holding an exclusive lock.
95    /// Implementations are not required to implement poisoning.
96    #[inline]
97    pub fn read(&self) -> RwLockReadGuard<'_, T> {
98        RwLockReadGuard({
99            #[cfg(feature = "parking_lot")]
100            {
101                self.0.read()
102            }
103            #[cfg(not(feature = "parking_lot"))]
104            self.0.read().unwrap()
105        })
106    }
107
108    /// Attempts to acquire this `RwLock` with shared read access without blocking.
109    ///
110    /// Returns `Some(guard)` if the lock was acquired, or `None` if it is already
111    /// held by a writer.
112    #[inline]
113    pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
114        #[cfg(feature = "parking_lot")]
115        {
116            self.0.try_read().map(RwLockReadGuard)
117        }
118        #[cfg(not(feature = "parking_lot"))]
119        {
120            match self.0.try_read() {
121                Ok(guard) => Some(RwLockReadGuard(guard)),
122                Err(std::sync::TryLockError::WouldBlock) => None,
123                Err(std::sync::TryLockError::Poisoned(err)) => panic!("{}", err),
124            }
125        }
126    }
127
128    /// Attempts to acquire this `RwLock` with exclusive write access without blocking.
129    ///
130    /// Returns `Some(guard)` if the lock was acquired, or `None` if it is already
131    /// held by any readers or a writer.
132    #[inline]
133    pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
134        #[cfg(feature = "parking_lot")]
135        {
136            self.0.try_write().map(RwLockWriteGuard)
137        }
138        #[cfg(not(feature = "parking_lot"))]
139        {
140            match self.0.try_write() {
141                Ok(guard) => Some(RwLockWriteGuard(guard)),
142                Err(std::sync::TryLockError::WouldBlock) => None,
143                Err(std::sync::TryLockError::Poisoned(err)) => panic!("{}", err),
144            }
145        }
146    }
147
148    /// Locks this `RwLock` with exclusive write access, blocking the current
149    /// thread until it can be acquired.
150    ///
151    /// This function will not return while other writers or other readers
152    /// currently have access to the lock.
153    ///
154    /// Returns an RAII guard which will drop the write access of this `RwLock`
155    /// when dropped.
156    ///
157    /// # Panics
158    ///
159    /// This function might panic when called if the lock is already held by the
160    /// current thread, or if the `RwLock` is poisoned. An `RwLock` might be
161    /// poisoned whenever a writer panics while holding an exclusive lock.
162    /// Implementations are not required to implement poisoning.
163    #[inline]
164    pub fn write(&self) -> RwLockWriteGuard<'_, T> {
165        RwLockWriteGuard({
166            #[cfg(feature = "parking_lot")]
167            {
168                self.0.write()
169            }
170            #[cfg(not(feature = "parking_lot"))]
171            self.0.write().unwrap()
172        })
173    }
174}
175
176impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
177    type Target = T;
178
179    fn deref(&self) -> &Self::Target {
180        &self.0
181    }
182}
183
184impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
185    type Target = T;
186
187    fn deref(&self) -> &Self::Target {
188        &self.0
189    }
190}
191
192impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
193    fn deref_mut(&mut self) -> &mut Self::Target {
194        &mut self.0
195    }
196}