Skip to main content

core_crypto_keystore/
unique_arc.rs

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