readlock_tokio/
lite.rs

1//! Versions of `Shared` and `SharedReadLock` that are implemented in terms of
2//! the [rclite] crate. Because [`rclite::Arc`] doesn't have weak references,
3//! there is no `WeakReadLock` here.
4
5use std::{fmt, ops};
6
7use rclite::Arc;
8use tokio::sync::RwLock;
9
10use crate::{readguard_into_ref, SharedReadGuard, SharedWriteGuard, TryLockError, TryLockResult};
11
12/// A wrapper around a resource possibly shared with [`SharedReadLock`]s, but no
13/// other `Shared`s.
14pub struct Shared<T>(Arc<RwLock<T>>);
15
16impl<T> Shared<T> {
17    /// Create a new `Shared`.
18    pub fn new(data: T) -> Self {
19        Self(Arc::new(RwLock::new(data)))
20    }
21
22    /// Returns the inner value, if the `Shared` has no associated
23    /// `SharedReadLock`s.
24    ///
25    /// Otherwise, an `Err` is returned with the same `Shared` that was passed
26    /// in.
27    ///
28    /// This will succeed even if there are outstanding weak references.
29    ///
30    /// # Panics
31    ///
32    /// This function will panic if the lock around the inner value is poisoned.
33    pub fn unwrap(this: Self) -> Result<T, Self> {
34        match Arc::try_unwrap(this.0) {
35            Ok(rwlock) => Ok(rwlock.into_inner()),
36            Err(arc) => Err(Self(arc)),
37        }
38    }
39
40    /// Get a reference to the inner value.
41    ///
42    /// Usually, you don't need to call this function since `Shared<T>`
43    /// implements `Deref`. Use this if you want to pass the inner value to a
44    /// generic function where the compiler can't infer that you want to have
45    /// the `Shared` dereferenced otherwise.
46    #[track_caller]
47    pub fn get(this: &Self) -> &T {
48        let read_guard =
49            this.0.try_read().expect("nothing else can hold a write lock at this time");
50        unsafe { readguard_into_ref(read_guard) }
51    }
52
53    /// Lock this `Shared` to be able to mutate it, causing the current task to
54    /// yield until the lock has been acquired.
55    pub async fn lock(this: &mut Self) -> SharedWriteGuard<'_, T> {
56        SharedWriteGuard(this.0.write().await)
57    }
58
59    /// Get a [`SharedReadLock`] for accessing the same resource read-only from
60    /// elsewhere.
61    pub fn get_read_lock(this: &Self) -> SharedReadLock<T> {
62        SharedReadLock(this.0.clone())
63    }
64}
65
66impl<T> ops::Deref for Shared<T> {
67    type Target = T;
68
69    fn deref(&self) -> &Self::Target {
70        Shared::get(self)
71    }
72}
73
74impl<T: fmt::Debug> fmt::Debug for Shared<T> {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        self.0.fmt(f)
77    }
78}
79
80/// A read-only reference to a resource possibly shared with up to one
81/// [`Shared`] and many other [`SharedReadLock`]s.
82#[derive(Clone)]
83pub struct SharedReadLock<T>(Arc<RwLock<T>>);
84
85impl<T> SharedReadLock<T> {
86    /// Lock this `SharedReadLock`, causing the current task to
87    /// yield until the lock has been acquired.
88    pub async fn lock(&self) -> SharedReadGuard<'_, T> {
89        SharedReadGuard(self.0.read().await)
90    }
91
92    /// Try to lock this `SharedReadLock`.
93    ///
94    /// If the value is currently locked for writing through the corresponding
95    /// `Shared` instance, returns [`TryLockError`].
96    pub fn try_lock(&self) -> TryLockResult<SharedReadGuard<'_, T>> {
97        self.0.try_read().map(SharedReadGuard).map_err(TryLockError)
98    }
99}
100
101impl<T: fmt::Debug> fmt::Debug for SharedReadLock<T> {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        self.0.fmt(f)
104    }
105}