core_crypto_keystore/
unique_arc.rs1use 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#[derive(Debug, derive_more::Deref)]
20pub struct UniqueArc<T> {
21 #[deref(forward)]
22 arc: Arc<T>,
23 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 pub async fn into_inner(this: Self) -> T {
43 let UniqueArc { arc, gate } = this;
44 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 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#[derive(Debug, derive_more::Deref)]
70pub(crate) struct ArcWithReadGuard<T> {
71 #[deref(forward)]
72 t: Arc<T>,
73 _guard: RwLockReadGuardArc<()>,
74}
75
76#[derive(Debug)]
81pub struct UniqueWeak<T> {
82 weak: Weak<T>,
83 gate: Arc<RwLock<()>>,
86}
87
88impl<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 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 pub async fn upgrade(&self) -> Option<impl Deref<Target = T>> {
114 self.upgrade_without_type_erasure().await
115 }
116
117 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 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 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 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 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 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 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 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 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 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 assert_eq!(weak.upgrade().await.as_deref(), Some(&42));
241 assert_eq!(weak.upgrade_sync().as_deref(), Some(&42));
242
243 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 assert!(weak.upgrade().await.is_none());
258 assert!(weak.upgrade_sync().is_none());
259 });
260 }
261
262 #[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 let holding = Semaphore::new(0); let proceed = Semaphore::new(0); 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 holding.add_permits(1);
282 proceed.acquire().await;
283 assert!(
285 !unpacked.load(Ordering::SeqCst),
286 "into_inner unpacked while an upgrade was still live"
287 );
288 *guard
289 };
291
292 let take = async {
293 holding.acquire().await;
295 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}