Skip to main content

core_crypto_keystore/
unique_arc.rs

1use std::{
2    borrow::Borrow,
3    ops::Deref,
4    sync::{Arc, Weak},
5};
6
7use async_lock::{RwLock, RwLockReadGuardArc};
8use async_trait::async_trait;
9
10use crate::{
11    CryptoKeystoreResult,
12    traits::{
13        BorrowPrimaryKey, Entity, EntityGetBorrowed, FetchFromDatabase, KeyType, SearchableEntity, UniqueEntityExt,
14    },
15};
16
17/// A smart pointer which has exactly one strong reference to the inner type,
18/// but may have several weak references.
19///
20/// This type is intentionally `!Clone` so that we know for a fact that it can be
21/// safely unpacked.
22#[derive(Debug, derive_more::Deref)]
23pub struct UniqueArc<T> {
24    #[deref(forward)]
25    arc: Arc<T>,
26    /// Coordinates temporary upgrades (shared/read) against final unpacking (exclusive/write).
27    gate: Arc<RwLock<()>>,
28}
29
30impl<T> From<T> for UniqueArc<T> {
31    fn from(value: T) -> Self {
32        UniqueArc {
33            arc: Arc::new(value),
34            gate: Arc::new(RwLock::new(())),
35        }
36    }
37}
38
39impl<T> UniqueArc<T> {
40    /// Unpack this `UniqueArc`, returning the inner value.
41    ///
42    /// This waits for any in-flight `UniqueWeak::upgrade` / `UniqueWeak::upgrade_sync`
43    /// guard to drop before unpacking, so that the sole remaining strong reference is the one
44    /// held by this `UniqueArc`.
45    pub async fn into_inner(this: Self) -> T {
46        let UniqueArc { arc, gate } = this;
47        // Wait for all outstanding upgrades to release their shared lease, and prevent new ones,
48        // before unpacking. This guard is not held across an `.await`, so synchronous upgrades on a
49        // single-threaded runtime can never observe it mid-unpack.
50        let _exclusive = gate.write().await;
51        Arc::into_inner(arc)
52            .expect("no other strong reference exists: `UniqueArc` is `!Clone` and all upgrades are gated")
53    }
54
55    /// Produce a weak reference to this item
56    pub fn downgrade(this: &Self) -> UniqueWeak<T> {
57        UniqueWeak {
58            weak: Arc::downgrade(&this.arc),
59            gate: Arc::clone(&this.gate),
60        }
61    }
62}
63
64/// This type derefs to `T`, and holds the read guard for its lifetime, ensuring that
65/// callers of [`UniqueWeak::upgrade`] neither remove the guard before the reads are complete
66/// nor have the capability to clone additional instances of `Arc<T>`.
67///
68/// Field order matters here: struct fields drop in declaration order, so `t` (the strong
69/// reference) must come before `_guard` (the read lease). Otherwise the lease would be released
70/// while a strong reference is still live, letting [`UniqueArc::into_inner`] acquire the write
71/// lease and panic on a doubled strong count.
72#[derive(Debug, derive_more::Deref)]
73pub(crate) struct ArcWithReadGuard<T> {
74    #[deref(forward)]
75    t: Arc<T>,
76    _guard: RwLockReadGuardArc<()>,
77}
78
79/// A weak version of [`UniqueArc`] which holds a non-owning, non-lifetimed reference to the managed allocation.
80///
81/// This allocation is accessed by calling `upgrade` or `upgrade_sync` on the `UniqueWeak`, each of which
82/// produces a guard which derefs to the contained type.
83#[derive(Debug)]
84pub struct UniqueWeak<T> {
85    weak: Weak<T>,
86    /// Shared with the originating [`UniqueArc`]; a shared lease is held for the duration of each
87    /// upgrade so that [`UniqueArc::into_inner`] cannot unpack while a temporary reference is alive.
88    gate: Arc<RwLock<()>>,
89}
90
91/// The derived version would have a bound `T: Clone` which we don't want
92impl<T> Clone for UniqueWeak<T> {
93    fn clone(&self) -> Self {
94        Self {
95            weak: self.weak.clone(),
96            gate: self.gate.clone(),
97        }
98    }
99}
100
101impl<T> UniqueWeak<T> {
102    /// Gain access to the wrapped member if it is available.
103    ///
104    /// Prefer [`Self::upgrade`] except where critical. You'll know if you have to have
105    /// a concrete type here. Mostly the type-erased version is better.
106    pub(crate) async fn upgrade_without_type_erasure(&self) -> Option<ArcWithReadGuard<T>> {
107        let _guard = self.gate.read_arc().await;
108        self.weak.upgrade().map(move |t| ArcWithReadGuard { _guard, t })
109    }
110
111    /// Gain access to the wrapped member if it is available.
112    ///
113    /// Note that the return type here is `impl Deref<Target = T>`.
114    /// This intentionally masks the real concrete type in order to prevent
115    /// receivers from increasing the internal Arc's strong count.
116    pub async fn upgrade(&self) -> Option<impl Deref<Target = T>> {
117        self.upgrade_without_type_erasure().await
118    }
119
120    /// Synchronously gain access to the wrapped member if it is available.
121    ///
122    /// Unlike [`Self::upgrade`], this method cannot suspend, so it takes the
123    /// shared guard with `try_read` and bails with `None` if the guard is held.
124    /// This is a racy snapshot rather than a definitive check, because if
125    /// `into_inner`'s future is dropped while parked waiting for readers to drain,
126    /// the value survives, yet this call already reported it gone.
127    pub fn upgrade_sync(&self) -> Option<impl Deref<Target = T>> {
128        let _guard = self.gate.try_read_arc()?;
129        self.weak.upgrade().map(move |t| ArcWithReadGuard { _guard, t })
130    }
131}
132
133#[cfg_attr(target_os = "unknown", async_trait(?Send))]
134#[cfg_attr(not(target_os = "unknown"), async_trait)]
135impl<T> FetchFromDatabase for UniqueArc<T>
136where
137    T: FetchFromDatabase,
138{
139    /// Get an instance of `E` from the database by its primary key.
140    async fn get<E>(&self, id: &E::PrimaryKey) -> CryptoKeystoreResult<Option<E>>
141    where
142        E: 'static + Entity + Clone + Send + Sync,
143    {
144        <T as FetchFromDatabase>::get::<E>(&self.arc, id).await
145    }
146
147    /// Count the number of `E`s in the database.
148    async fn count<E>(&self) -> CryptoKeystoreResult<u32>
149    where
150        E: 'static + Entity + Clone + Send + Sync,
151    {
152        <T as FetchFromDatabase>::count::<E>(&self.arc).await
153    }
154
155    /// Load all `E`s from the database.
156    async fn load_all<E>(&self) -> CryptoKeystoreResult<Vec<E>>
157    where
158        E: 'static + Entity + Clone + Send + Sync,
159    {
160        <T as FetchFromDatabase>::load_all::<E>(&self.arc).await
161    }
162
163    /// Get an instance of `E` from the database by the borrowed form of its primary key.
164    async fn get_borrowed<E>(&self, id: &<E as BorrowPrimaryKey>::BorrowedPrimaryKey) -> CryptoKeystoreResult<Option<E>>
165    where
166        E: 'static + EntityGetBorrowed + Clone + Send + Sync,
167        E::PrimaryKey: Borrow<E::BorrowedPrimaryKey>,
168        for<'a> &'a E::BorrowedPrimaryKey: KeyType,
169    {
170        <T as FetchFromDatabase>::get_borrowed::<E>(&self.arc, id).await
171    }
172
173    /// Get the requested unique entity from the database.
174    async fn get_unique<'a, U>(&self) -> CryptoKeystoreResult<Option<U>>
175    where
176        U: 'static + UniqueEntityExt + Entity + Clone + Send + Sync,
177    {
178        <T as FetchFromDatabase>::get_unique(&self.arc).await
179    }
180
181    /// Determine whether a unique entity is present in the database.
182    async fn exists<'a, U>(&self) -> CryptoKeystoreResult<bool>
183    where
184        U: 'static + UniqueEntityExt + Entity + Clone + Send + Sync,
185    {
186        <T as FetchFromDatabase>::exists::<U>(&self.arc).await
187    }
188
189    /// Search for relevant instances of `E` given a search key.
190    async fn search<E, SearchKey>(&self, search_key: &SearchKey) -> CryptoKeystoreResult<Vec<E>>
191    where
192        E: 'static + Entity + SearchableEntity<SearchKey> + Clone + Send + Sync,
193        SearchKey: KeyType,
194    {
195        <T as FetchFromDatabase>::search::<E, SearchKey>(&self.arc, search_key).await
196    }
197}
198
199#[cfg(all(test, not(target_os = "unknown")))]
200mod tests {
201    use std::sync::atomic::{AtomicBool, Ordering};
202
203    use async_lock::Semaphore;
204    use futures_lite::future;
205
206    use super::UniqueArc;
207
208    #[test]
209    fn derefs_to_the_inner_value() {
210        let arc = UniqueArc::from(vec![1, 2, 3]);
211        // method resolves on `T` via the forwarded `Deref`
212        assert_eq!(arc.len(), 3);
213        assert_eq!(&*arc, &[1, 2, 3]);
214    }
215
216    #[test]
217    fn into_inner_returns_the_value() {
218        future::block_on(async {
219            let arc = UniqueArc::from(String::from("hello"));
220            assert_eq!(UniqueArc::into_inner(arc).await, "hello");
221        });
222    }
223
224    #[test]
225    fn outstanding_weaks_do_not_block_unpacking() {
226        future::block_on(async {
227            // A `UniqueWeak` that never upgrades holds no strong reference, so it must not prevent
228            // `into_inner` from succeeding: weak references are fine, only live upgrades are not.
229            let arc = UniqueArc::from(7_u32);
230            let _weak = UniqueArc::downgrade(&arc);
231            assert_eq!(UniqueArc::into_inner(arc).await, 7);
232        });
233    }
234
235    #[test]
236    fn upgrade_sees_the_value_while_alive() {
237        future::block_on(async {
238            let arc = UniqueArc::from(42_u32);
239            let weak = UniqueArc::downgrade(&arc);
240
241            // both variants hand back a guard that derefs to the live value
242            assert_eq!(weak.upgrade().await.as_deref(), Some(&42));
243            assert_eq!(weak.upgrade_sync().as_deref(), Some(&42));
244
245            // keep `arc` alive until here
246            assert_eq!(*arc, 42);
247        });
248    }
249
250    #[test]
251    fn upgrade_fails_once_unpacked() {
252        future::block_on(async {
253            let arc = UniqueArc::from(42_u32);
254            let weak = UniqueArc::downgrade(&arc);
255
256            assert_eq!(UniqueArc::into_inner(arc).await, 42);
257
258            // the allocation is gone, so both variants report the value as unavailable
259            assert!(weak.upgrade().await.is_none());
260            assert!(weak.upgrade_sync().is_none());
261        });
262    }
263
264    /// Regression test for the original soundness bug: an in-flight upgrade holds a temporary
265    /// strong reference (behind the returned guard) across an `.await`, so a naive `into_inner`
266    /// would observe two strong references and panic. `into_inner` must instead wait for the guard
267    /// to be dropped.
268    #[test]
269    fn into_inner_waits_for_an_in_flight_upgrade() {
270        future::block_on(async {
271            let arc = UniqueArc::from(42_u32);
272            let weak = UniqueArc::downgrade(&arc);
273
274            // one-shot signals between the two concurrent futures
275            let holding = Semaphore::new(0); // upgrade -> take: "I hold a temporary strong ref"
276            let proceed = Semaphore::new(0); // take -> upgrade: "you may release it now"
277            let unpacked = AtomicBool::new(false);
278
279            let upgrade = async {
280                let guard = weak.upgrade().await.expect("value is still alive");
281                assert_eq!(*guard, 42);
282                // announce that a temporary strong reference is now live, then hold it
283                holding.add_permits(1);
284                proceed.acquire().await;
285                // `into_inner` must not have unpacked while we still hold the guard
286                assert!(
287                    !unpacked.load(Ordering::SeqCst),
288                    "into_inner unpacked while an upgrade was still live"
289                );
290                *guard
291                // `guard` drops here, releasing the strong reference and then the read lease
292            };
293
294            let take = async {
295                // wait until the upgrade is definitely holding its strong reference ...
296                holding.acquire().await;
297                // ... release it, then take. `into_inner` must wait for the guard to drop
298                // rather than panicking on the transiently-doubled strong count.
299                proceed.add_permits(1);
300                let value = UniqueArc::into_inner(arc).await;
301                unpacked.store(true, Ordering::SeqCst);
302                value
303            };
304
305            let (upgraded, taken) = future::zip(upgrade, take).await;
306            assert_eq!(upgraded, 42);
307            assert_eq!(taken, 42);
308        });
309    }
310}