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>;