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