fuser_async/
rwlockoption.rs

1//! Implementation of [`RwLockOption<T>`], with a similar interface as [`tokio::sync::OnceCell`].
2
3use std::sync::Arc;
4
5#[derive(Clone)]
6pub struct RwLockOption<T>(Arc<tokio::sync::RwLock<Option<T>>>);
7pub type RwLockOptionGuard<T> = tokio::sync::OwnedRwLockReadGuard<Option<T>, T>;
8pub type RwLockOptionWriteGuard<T> = tokio::sync::OwnedRwLockMappedWriteGuard<Option<T>, T>;
9impl<T> RwLockOption<T> {
10    pub async fn get_or_try_init<E, F: std::future::Future<Output = Result<T, E>>>(
11        &self,
12        f: impl FnOnce() -> F,
13    ) -> Result<RwLockOptionGuard<T>, E> {
14        let s = self.0.clone().read_owned().await;
15        let s = if s.is_none() {
16            drop(s);
17            let mut s = self.0.clone().write_owned().await;
18            if s.is_none() {
19                *s = Some(f().await?);
20            }
21            // Don't use `downgrade`, because that would create a deadlock with other potential
22            // accesses to the function above, trying to acquire a write lock.
23            // TODO: Add a test
24            // See
25            // https://docs.rs/tokio/latest/tokio/sync/struct.RwLock.html#method.read
26            // https://docs.rs/tokio/latest/tokio/sync/struct.RwLockWriteGuard.html#method.downgrade
27            drop(s);
28            self.0.clone().read_owned().await
29        } else {
30            s
31        };
32        Ok(tokio::sync::OwnedRwLockReadGuard::map(s, |x| {
33            x.as_ref().unwrap()
34        }))
35    }
36    pub async fn get_mut_or_try_init<E, F: std::future::Future<Output = Result<T, E>>>(
37        &self,
38        f: impl FnOnce() -> F,
39    ) -> Result<RwLockOptionWriteGuard<T>, E> {
40        let mut s = self.0.clone().write_owned().await;
41        if s.is_none() {
42            *s = Some(f().await?);
43        }
44        Ok(tokio::sync::OwnedRwLockWriteGuard::map(s, |s| match s {
45            Some(s) => s,
46            None => unreachable!(),
47        }))
48    }
49    pub fn is_some(&self) -> Option<bool> {
50        self.0.try_read().ok().map(|s| s.is_some())
51    }
52    pub fn try_read(&self) -> Option<RwLockOptionGuard<T>> {
53        let data = self.0.clone().try_read_owned().ok()?;
54        if data.is_none() {
55            return None;
56        }
57        Some(tokio::sync::OwnedRwLockReadGuard::map(data, |x| {
58            x.as_ref().unwrap()
59        }))
60    }
61    pub async fn clear(&self) -> anyhow::Result<()> {
62        let mut s = self.0.try_write()?;
63        s.take();
64        Ok(())
65    }
66    pub fn new() -> Self {
67        Self(Arc::new(tokio::sync::RwLock::new(None)))
68    }
69}
70impl<T> Default for RwLockOption<T> {
71    fn default() -> Self {
72        Self::new()
73    }
74}