readlock_tokio/lib.rs
1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3
4use std::{
5 fmt, ops,
6 sync::{Arc, Weak},
7};
8use tokio::sync::{OwnedRwLockReadGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
9
10#[cfg(feature = "lite")]
11pub mod lite;
12
13/// A wrapper around a resource possibly shared with [`SharedReadLock`]s and
14/// [`WeakReadLock`]s, but no other `Shared`s.
15pub struct Shared<T: ?Sized>(Arc<RwLock<T>>);
16
17#[allow(clippy::arc_with_non_send_sync)] // should not fire for generics
18impl<T> Shared<T> {
19 /// Create a new `Shared`.
20 pub fn new(data: T) -> Self {
21 Self(Arc::new(RwLock::new(data)))
22 }
23
24 /// Returns the inner value, if the `Shared` has no associated
25 /// `SharedReadLock`s.
26 ///
27 /// Otherwise, an `Err` is returned with the same `Shared` that was passed
28 /// in.
29 ///
30 /// This will succeed even if there are outstanding weak references.
31 ///
32 /// # Panics
33 ///
34 /// This function will panic if the lock around the inner value is poisoned.
35 pub fn unwrap(this: Self) -> Result<T, Self> {
36 match Arc::try_unwrap(this.0) {
37 Ok(rwlock) => Ok(rwlock.into_inner()),
38 Err(arc) => Err(Self(arc)),
39 }
40 }
41}
42
43impl<T: ?Sized> Shared<T> {
44 /// Get a reference to the inner value.
45 ///
46 /// Usually, you don't need to call this function since `Shared<T>`
47 /// implements `Deref`. Use this if you want to pass the inner value to a
48 /// generic function where the compiler can't infer that you want to have
49 /// the `Shared` dereferenced otherwise.
50 pub fn get(this: &Self) -> &T {
51 let read_guard =
52 this.0.try_read().expect("nothing else can hold a write lock at this time");
53 unsafe { readguard_into_ref(read_guard) }
54 }
55
56 /// Lock this `Shared` to be able to mutate it, causing the current task to
57 /// yield until the lock has been acquired.
58 pub async fn lock(this: &mut Self) -> SharedWriteGuard<'_, T> {
59 SharedWriteGuard(this.0.write().await)
60 }
61
62 /// Get a [`SharedReadLock`] for accessing the same resource read-only from
63 /// elsewhere.
64 pub fn get_read_lock(this: &Self) -> SharedReadLock<T> {
65 SharedReadLock(this.0.clone())
66 }
67
68 /// Attempt to create a `Shared` from its internal representation,
69 /// `Arc<RwLock<T>>`.
70 ///
71 /// This returns `Ok(_)` only if there are no further references (including
72 /// weak references) to the inner `RwLock` since otherwise, `Shared`s
73 /// invariant of being the only instance that can mutate the inner value
74 /// would be broken.
75 pub fn try_from_inner(rwlock: Arc<RwLock<T>>) -> Result<Self, Arc<RwLock<T>>> {
76 if Arc::strong_count(&rwlock) == 1 && Arc::weak_count(&rwlock) == 0 {
77 Ok(Self(rwlock))
78 } else {
79 Err(rwlock)
80 }
81 }
82
83 /// Turns this `Shared` into its internal representation, `Arc<RwLock<T>>`.
84 pub fn into_inner(this: Self) -> Arc<RwLock<T>> {
85 this.0
86 }
87
88 /// Gets the number of associated [`SharedReadLock`]s.
89 pub fn read_count(this: &Self) -> usize {
90 Arc::strong_count(&this.0) - 1
91 }
92
93 /// Gets the number of associated [`WeakReadLock`]s.
94 pub fn weak_count(this: &Self) -> usize {
95 Arc::weak_count(&this.0)
96 }
97}
98
99/// SAFETY: Only allowed for a read guard obtained from the inner value of a
100/// `Shared`. Transmuting lifetime here, this is okay because the resulting
101/// reference's borrows this, which is the only `Shared` instance that could
102/// mutate the inner value (you can not have two `Shared`s that reference the
103/// same inner value) and the other references that can exist to the inner value
104/// are only allowed to read as well.
105unsafe fn readguard_into_ref<'a, T: ?Sized + 'a>(guard: RwLockReadGuard<'a, T>) -> &'a T {
106 let reference: &T = &guard;
107 &*(reference as *const T)
108}
109
110impl<T: ?Sized> ops::Deref for Shared<T> {
111 type Target = T;
112
113 fn deref(&self) -> &Self::Target {
114 Shared::get(self)
115 }
116}
117
118impl<T: fmt::Debug + ?Sized> fmt::Debug for Shared<T> {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 self.0.fmt(f)
121 }
122}
123
124impl<T: Default> Default for Shared<T> {
125 fn default() -> Self {
126 Self::new(T::default())
127 }
128}
129
130/// A read-only reference to a resource possibly shared with up to one
131/// [`Shared`] and many [`WeakReadLock`]s.
132pub struct SharedReadLock<T: ?Sized>(Arc<RwLock<T>>);
133
134impl<T: ?Sized> SharedReadLock<T> {
135 /// Lock this `SharedReadLock`, causing the current task to yield until the
136 /// lock has been acquired.
137 pub async fn lock(&self) -> SharedReadGuard<'_, T> {
138 SharedReadGuard(self.0.read().await)
139 }
140
141 /// Try to lock this `SharedReadLock`.
142 ///
143 /// If the value is currently locked for writing through the corresponding
144 /// `Shared` instance, returns [`TryLockError`].
145 pub fn try_lock(&self) -> TryLockResult<SharedReadGuard<'_, T>> {
146 self.0.try_read().map(SharedReadGuard).map_err(TryLockError)
147 }
148
149 /// Lock this `SharedReadLock`, causing the current task to yield until the
150 /// lock has been acquired.
151 ///
152 /// This method is identical to [`lock`][Self::lock], except that the
153 /// returned guard keeps a clone of the internal [`Arc`] instead of
154 /// borrowing it. Therefore, the guard does has a `'static` lifetime.
155 pub async fn lock_owned(self) -> OwnedSharedReadGuard<T> {
156 OwnedSharedReadGuard(self.0.read_owned().await)
157 }
158
159 /// Create a new [`WeakReadLock`] pointer to this allocation.
160 pub fn downgrade(&self) -> WeakReadLock<T> {
161 WeakReadLock(Arc::downgrade(&self.0))
162 }
163
164 /// Upgrade a `SharedReadLock` to `Shared`.
165 ///
166 /// This only return `Ok(_)` if there are no other references (including a
167 /// `Shared`, or weak references) to the inner value, since otherwise it
168 /// would be possible to have multiple `Shared`s for the same inner value
169 /// alive at the same time, which would violate `Shared`s invariant of
170 /// being the only reference that is able to mutate the inner value.
171 pub fn try_upgrade(self) -> Result<Shared<T>, Self> {
172 if Arc::strong_count(&self.0) == 1 && Arc::weak_count(&self.0) == 0 {
173 Ok(Shared(self.0))
174 } else {
175 Err(self)
176 }
177 }
178
179 /// Create a `SharedReadLock` from its internal representation,
180 /// `Arc<RwLock<T>>`.
181 ///
182 /// You can use this to create a `SharedReadLock` from a shared `RwLock`
183 /// without ever using `Shared`, if you want to expose an API where there is
184 /// a value that can be written only from inside one module or crate, but
185 /// outside users should be allowed to obtain a reusable lock for reading
186 /// the inner value.
187 pub fn from_inner(rwlock: Arc<RwLock<T>>) -> Self {
188 Self(rwlock)
189 }
190
191 /// Attempt to turn this `SharedReadLock` into its internal representation,
192 /// `Arc<RwLock<T>>`.
193 ///
194 /// This returns `Ok(_)` only if there are no further references (including
195 /// a `Shared`, or weak references) to the inner value, since otherwise
196 /// it would be possible to have a `Shared` and an `Arc<RwLock<T>>` for
197 /// the same inner value alive at the same time, which would violate
198 /// `Shared`s invariant of being the only reference that is able to
199 /// mutate the inner value.
200 pub fn try_into_inner(self) -> Result<Arc<RwLock<T>>, Self> {
201 if Arc::strong_count(&self.0) == 1 && Arc::weak_count(&self.0) == 0 {
202 Ok(self.0)
203 } else {
204 Err(self)
205 }
206 }
207}
208
209impl<T: ?Sized> Clone for SharedReadLock<T> {
210 fn clone(&self) -> Self {
211 Self(Arc::clone(&self.0))
212 }
213}
214
215impl<T: fmt::Debug + ?Sized> fmt::Debug for SharedReadLock<T> {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 self.0.fmt(f)
218 }
219}
220
221/// A weak read-only reference to a resource possibly shared with up to one
222/// [`Shared`], and many [`SharedReadLock`]s.
223pub struct WeakReadLock<T: ?Sized>(Weak<RwLock<T>>);
224
225impl<T: ?Sized> WeakReadLock<T> {
226 /// Attempt to upgrade the `WeakReadLock` into a `SharedReadLock`, delaying
227 /// dropping of the inner value if successful.
228 ///
229 /// Returns `None` if the inner value has already been dropped.
230 pub fn upgrade(&self) -> Option<SharedReadLock<T>> {
231 Weak::upgrade(&self.0).map(SharedReadLock)
232 }
233}
234
235impl<T: ?Sized> Clone for WeakReadLock<T> {
236 fn clone(&self) -> Self {
237 Self(Weak::clone(&self.0))
238 }
239}
240
241impl<T: fmt::Debug + ?Sized> fmt::Debug for WeakReadLock<T> {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 self.0.fmt(f)
244 }
245}
246
247/// RAII structure used to release the shared read access of a lock when
248/// dropped.
249#[clippy::has_significant_drop]
250pub struct SharedReadGuard<'a, T: ?Sized>(RwLockReadGuard<'a, T>);
251
252impl<'a, T: ?Sized + 'a> SharedReadGuard<'a, T> {
253 /// Create a `SharedReadGuard` from its internal representation,
254 /// `RwLockReadGuard<'a, T>`.
255 pub fn from_inner(guard: RwLockReadGuard<'a, T>) -> Self {
256 Self(guard)
257 }
258}
259
260impl<'a, T: ?Sized + 'a> ops::Deref for SharedReadGuard<'a, T> {
261 type Target = T;
262
263 fn deref(&self) -> &Self::Target {
264 &self.0
265 }
266}
267
268impl<'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for SharedReadGuard<'a, T> {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 self.0.fmt(f)
271 }
272}
273
274/// RAII structure used to release the shared read access of a lock when
275/// dropped.
276#[clippy::has_significant_drop]
277pub struct OwnedSharedReadGuard<T: ?Sized>(OwnedRwLockReadGuard<T>);
278
279impl<T: ?Sized> OwnedSharedReadGuard<T> {
280 /// Create a `SharedReadGuard` from its internal representation,
281 /// `OwnedRwLockReadGuard< T>`.
282 pub fn from_inner(guard: OwnedRwLockReadGuard<T>) -> Self {
283 Self(guard)
284 }
285}
286
287impl<T: ?Sized> ops::Deref for OwnedSharedReadGuard<T> {
288 type Target = T;
289
290 fn deref(&self) -> &Self::Target {
291 &self.0
292 }
293}
294
295impl<T: fmt::Debug + ?Sized> fmt::Debug for OwnedSharedReadGuard<T> {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 self.0.fmt(f)
298 }
299}
300
301/// RAII structure used to release the exclusive write access of a lock when
302/// dropped.
303#[clippy::has_significant_drop]
304pub struct SharedWriteGuard<'a, T: ?Sized>(RwLockWriteGuard<'a, T>);
305
306impl<'a, T: ?Sized + 'a> ops::Deref for SharedWriteGuard<'a, T> {
307 type Target = T;
308
309 fn deref(&self) -> &Self::Target {
310 &self.0
311 }
312}
313
314impl<'a, T: ?Sized> SharedWriteGuard<'a, T> {
315 /// Create a `SharedWriteGuard` from its internal representation,
316 /// `RwLockWriteGuard<'a, T>`.
317 pub fn from_inner(guard: RwLockWriteGuard<'a, T>) -> Self {
318 Self(guard)
319 }
320}
321
322impl<'a, T: ?Sized + 'a> ops::DerefMut for SharedWriteGuard<'a, T> {
323 fn deref_mut(&mut self) -> &mut Self::Target {
324 &mut self.0
325 }
326}
327
328impl<'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for SharedWriteGuard<'a, T> {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 self.0.fmt(f)
331 }
332}
333
334/// Error returned from [`SharedReadLock::try_lock`].
335pub struct TryLockError(tokio::sync::TryLockError);
336
337impl fmt::Display for TryLockError {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 self.0.fmt(f)
340 }
341}
342
343impl fmt::Debug for TryLockError {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 self.0.fmt(f)
346 }
347}
348
349impl std::error::Error for TryLockError {}
350
351/// A type alias for the result of a nonblocking locking method.
352pub type TryLockResult<T> = Result<T, TryLockError>;