1#![allow(dead_code)]
11
12use crate::sync_primitives::{RwLock, RwLockWriteGuard};
13#[cfg(feature = "async")]
14use futures::stream::{FuturesUnordered, StreamExt};
15use maybe_async::maybe_async;
16use std::collections::HashMap;
17use std::fmt::Debug;
18use std::hash::Hash;
19use std::sync::atomic::{AtomicUsize, Ordering};
20use std::sync::Arc;
21use std::{io, mem};
22use tracing::{error, instrument, trace};
23
24pub(crate) struct AsyncLruCacheEntry<V> {
26 value: Option<Arc<V>>,
30
31 last_used: AtomicUsize,
33}
34
35struct AsyncLruCacheInner<
37 Key: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync,
38 Value: Send + Sync,
39 IoBackend: AsyncLruCacheBackend<Key = Key, Value = Value>,
40> {
41 backend: IoBackend,
43
44 map: RwLock<HashMap<Key, AsyncLruCacheEntry<Value>>>,
46
47 lru_timer: AtomicUsize,
49
50 limit: usize,
52}
53
54pub(crate) struct AsyncLruCache<
61 K: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync,
62 V: Send + Sync,
63 B: AsyncLruCacheBackend<Key = K, Value = V>,
64>(Arc<AsyncLruCacheInner<K, V, B>>);
65
66#[maybe_async(AFIT)]
68pub(crate) trait AsyncLruCacheBackend: Send + Sync {
69 type Key: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync;
71 type Value: Send + Sync;
73
74 #[allow(async_fn_in_trait)] async fn load(&self, key: Self::Key) -> io::Result<Self::Value>;
77
78 #[allow(async_fn_in_trait)] async fn flush(&self, key: Self::Key, value: &Self::Value) -> io::Result<()>;
84
85 unsafe fn evict(&self, key: Self::Key, value: Self::Value);
95}
96
97#[maybe_async]
98impl<
99 K: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync,
100 V: Send + Sync,
101 B: AsyncLruCacheBackend<Key = K, Value = V>,
102 > AsyncLruCache<K, V, B>
103{
104 pub fn new(backend: B, size: usize) -> Self {
108 AsyncLruCache(Arc::new(AsyncLruCacheInner {
109 backend,
110 map: Default::default(),
111 lru_timer: AtomicUsize::new(0),
112 limit: size,
113 }))
114 }
115
116 pub async fn get_or_insert(&self, key: K, may_flush: bool) -> io::Result<Option<Arc<V>>> {
126 self.0.get_or_insert(key, may_flush).await
127 }
128
129 pub async fn insert(&self, key: K, value: Arc<V>, may_flush: bool) -> io::Result<bool> {
140 self.0.insert(key, value, may_flush).await
141 }
142
143 pub async fn flush(&self) -> io::Result<()> {
147 self.0.flush().await
148 }
149
150 pub async unsafe fn invalidate(&self) -> io::Result<()> {
158 unsafe { self.0.invalidate() }.await
159 }
160}
161
162#[maybe_async]
163impl<
164 K: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync,
165 V: Send + Sync,
166 B: AsyncLruCacheBackend<Key = K, Value = V>,
167 > AsyncLruCacheInner<K, V, B>
168{
169 #[instrument(
183 level = "trace",
184 name = "AsyncLruCache::ensure_free_entry",
185 skip_all,
186 fields(self = &self as *const _ as usize),
187 )]
188 async fn ensure_free_entry(
189 &self,
190 map: &mut RwLockWriteGuard<'_, HashMap<K, AsyncLruCacheEntry<V>>>,
191 may_evict: bool,
192 ) -> io::Result<bool> {
193 if map.len() < self.limit {
194 return Ok(true);
195 } else if !may_evict {
196 return Ok(false);
197 }
198
199 while map.len() >= self.limit {
200 trace!("{} / {} used", map.len(), self.limit);
201
202 let now = self.lru_timer.load(Ordering::Relaxed);
203 let oldest = map
204 .iter()
205 .filter(|(_key, entry)| Arc::strong_count(entry.value()) == 1)
206 .fold((0, None), |oldest, (key, entry)| {
207 assert_eq!(Arc::weak_count(entry.value()), 0);
211
212 let age = now.wrapping_sub(entry.last_used.load(Ordering::Relaxed));
213 if age >= oldest.0 {
214 (age, Some(*key))
215 } else {
216 oldest
217 }
218 });
219
220 let Some(oldest_key) = oldest.1 else {
221 error!("Cannot evict entry from cache; everything is in use");
222 return Err(io::Error::other(
223 "Cannot evict entry from cache; everything is in use",
224 ));
225 };
226
227 trace!("Removing entry with key {oldest_key:?}, aged {}", oldest.0);
228
229 let oldest_entry = map.remove(&oldest_key).unwrap();
230
231 let evicted_object = Arc::try_unwrap(oldest_entry.value.unwrap())
236 .unwrap_or_else(|_| panic!("entry has gained external references"));
237
238 trace!("Flushing {oldest_key:?}");
239 if let Err(err) = self.backend.flush(oldest_key, &evicted_object).await {
240 map.insert(
241 oldest_key,
242 AsyncLruCacheEntry {
243 value: Some(Arc::new(evicted_object)),
244 last_used: oldest_entry.last_used.load(Ordering::Relaxed).into(),
245 },
246 );
247 return Err(err);
248 }
249 }
250
251 Ok(true)
252 }
253
254 async fn get_or_insert(&self, key: K, may_flush: bool) -> io::Result<Option<Arc<V>>> {
266 {
267 let map = self.map.read().await;
268 if let Some(entry) = map.get(&key) {
269 entry.last_used.store(
270 self.lru_timer.fetch_add(1, Ordering::Relaxed),
271 Ordering::Relaxed,
272 );
273 return Ok(Some(Arc::clone(entry.value())));
274 }
275 }
276
277 let mut map = self.map.write().await;
278 if let Some(entry) = map.get(&key) {
279 entry.last_used.store(
280 self.lru_timer.fetch_add(1, Ordering::Relaxed),
281 Ordering::Relaxed,
282 );
283 return Ok(Some(Arc::clone(entry.value())));
284 }
285
286 if !self.ensure_free_entry(&mut map, may_flush).await? {
287 return Ok(None);
288 }
289
290 let object = Arc::new(self.backend.load(key).await?);
291
292 let new_entry = AsyncLruCacheEntry {
293 value: Some(Arc::clone(&object)),
294 last_used: AtomicUsize::new(self.lru_timer.fetch_add(1, Ordering::Relaxed)),
295 };
296 map.insert(key, new_entry);
297
298 Ok(Some(object))
299 }
300
301 async fn insert(&self, key: K, value: Arc<V>, may_flush: bool) -> io::Result<bool> {
312 let mut map = self.map.write().await;
313 if let Some(entry) = map.get_mut(&key) {
314 if !may_flush {
315 return Ok(false);
316 }
317
318 entry.last_used.store(
319 self.lru_timer.fetch_add(1, Ordering::Relaxed),
320 Ordering::Relaxed,
321 );
322 self.backend.flush(key, entry.value()).await?;
323 entry.value = Some(value);
324 } else {
325 if !self.ensure_free_entry(&mut map, may_flush).await? {
326 return Ok(false);
327 }
328
329 let new_entry = AsyncLruCacheEntry {
330 value: Some(value),
331 last_used: AtomicUsize::new(self.lru_timer.fetch_add(1, Ordering::Relaxed)),
332 };
333 map.insert(key, new_entry);
334 }
335
336 Ok(true)
337 }
338
339 #[instrument(
343 level = "trace",
344 name = "AsyncLruCache::flush",
345 skip_all,
346 fields(self = &self as *const _ as usize)
347 )]
348 async fn flush(&self) -> io::Result<()> {
349 #[cfg(feature = "async")]
350 let mut futs = FuturesUnordered::new();
351
352 let map = self.map.read().await;
353 let mut first_err: Option<io::Error> = None;
354 for (key, entry) in map.iter() {
355 let key = *key;
356 trace!("Flushing {key:?}");
357 #[cfg(feature = "async")]
358 futs.push({
359 let object = Arc::clone(entry.value());
360 async move { self.backend.flush(key, &object).await }
361 });
362 #[cfg(feature = "sync")]
363 if let Err(e) = self.backend.flush(key, entry.value()) {
364 first_err.get_or_insert(e);
365 }
366 }
367
368 #[cfg(feature = "async")]
369 while let Some(result) = futs.next().await {
370 if let Err(e) = result {
371 first_err.get_or_insert(e);
372 }
373 }
374 if let Some(e) = first_err {
375 Err(e)
376 } else {
377 Ok(())
378 }
379 }
380
381 #[instrument(
389 level = "trace",
390 name = "AsyncLruCache::invalidate",
391 skip_all,
392 fields(self = &self as *const _ as usize)
393 )]
394 async unsafe fn invalidate(&self) -> io::Result<()> {
395 let mut in_use = Vec::new();
396
397 let mut map = self.map.write().await;
398 let old_map = mem::take(&mut *map);
401 for (key, mut entry) in old_map {
402 let object = entry.value.take().unwrap();
403 trace!("Evicting {key:?}");
404 match Arc::try_unwrap(object) {
405 Ok(object) => {
406 unsafe { self.backend.evict(key, object) };
408 }
409
410 Err(arc) => {
411 trace!("Entry is still in use, retaining it");
412 entry.value = Some(arc);
413 map.insert(key, entry);
414 in_use.push(key);
415 }
416 }
417 }
418
419 if in_use.is_empty() {
420 Ok(())
421 } else {
422 Err(io::Error::other(format!(
423 "Cannot invalidate cache, entries still in use: {}",
424 in_use
425 .iter()
426 .map(|key| format!("{key:?}"))
427 .collect::<Vec<String>>()
428 .join(", "),
429 )))
430 }
431 }
432}
433
434impl<V> AsyncLruCacheEntry<V> {
435 fn value(&self) -> &Arc<V> {
437 self.value.as_ref().unwrap()
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444 use std::sync::atomic::AtomicUsize;
445
446 struct DummyBackend;
448
449 #[maybe_async(AFIT)]
450 impl AsyncLruCacheBackend for DummyBackend {
451 type Key = usize;
452 type Value = usize;
453
454 async fn load(&self, key: usize) -> io::Result<usize> {
455 Ok(key)
456 }
457
458 async fn flush(&self, _key: usize, _value: &usize) -> io::Result<()> {
459 Ok(())
460 }
461
462 unsafe fn evict(&self, _key: usize, _value: usize) {}
463 }
464
465 #[derive(Default)]
467 struct RecordingBackend {
468 flushed: std::sync::Mutex<Vec<(usize, usize)>>,
469 }
470
471 #[maybe_async(AFIT)]
472 impl AsyncLruCacheBackend for RecordingBackend {
473 type Key = usize;
474 type Value = usize;
475
476 async fn load(&self, key: usize) -> io::Result<usize> {
477 Ok(key)
478 }
479
480 async fn flush(&self, key: usize, value: &usize) -> io::Result<()> {
481 self.flushed.lock().unwrap().push((key, *value));
482 Ok(())
483 }
484
485 unsafe fn evict(&self, _key: usize, _value: usize) {}
486 }
487
488 #[maybe_async(AFIT)]
489 impl<B: AsyncLruCacheBackend> AsyncLruCacheBackend for Arc<B> {
490 type Key = <B as AsyncLruCacheBackend>::Key;
491 type Value = <B as AsyncLruCacheBackend>::Value;
492
493 async fn load(&self, key: Self::Key) -> io::Result<Self::Value> {
494 (**self).load(key).await
495 }
496
497 async fn flush(&self, key: Self::Key, value: &Self::Value) -> io::Result<()> {
498 (**self).flush(key, value).await
499 }
500
501 unsafe fn evict(&self, key: Self::Key, value: Self::Value) {
502 unsafe { (**self).evict(key, value) }
503 }
504 }
505
506 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
509 async fn test_flush_continues_past_errors() {
510 #[derive(Default)]
511 struct FailOddBackend {
512 flush_count: AtomicUsize,
513 }
514
515 #[maybe_async(AFIT)]
516 impl AsyncLruCacheBackend for FailOddBackend {
517 type Key = usize;
518 type Value = usize;
519
520 async fn load(&self, key: usize) -> io::Result<usize> {
521 Ok(key)
522 }
523
524 async fn flush(&self, key: usize, _value: &usize) -> io::Result<()> {
525 self.flush_count.fetch_add(1, Ordering::Relaxed);
526 if key % 2 == 1 {
527 Err(io::Error::other("odd key"))
528 } else {
529 Ok(())
530 }
531 }
532
533 unsafe fn evict(&self, _key: usize, _value: usize) {}
534 }
535
536 const ENTRIES: usize = 42;
537
538 let backend = Arc::new(FailOddBackend::default());
539 let cache = AsyncLruCache::new(Arc::clone(&backend), ENTRIES);
540
541 for i in 0..ENTRIES {
542 cache.get_or_insert(i, false).await.unwrap().unwrap();
543 }
544
545 let err = cache.flush().await.unwrap_err();
546 assert!(err.to_string().contains("odd key"));
547
548 assert_eq!(backend.flush_count.load(Ordering::Relaxed), ENTRIES);
549 }
550
551 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
553 async fn test_lru_eviction_order() {
554 const ENTRIES: usize = 3;
555
556 let backend = Arc::new(RecordingBackend::default());
557 let cache = AsyncLruCache::new(Arc::clone(&backend), ENTRIES);
558
559 for i in 0..ENTRIES {
560 cache.get_or_insert(i, false).await.unwrap().unwrap();
561 }
562
563 cache.get_or_insert(0, false).await.unwrap().unwrap();
565
566 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
568 assert_eq!(entry, None);
569 cache.get_or_insert(ENTRIES, true).await.unwrap().unwrap();
570
571 assert_eq!(*backend.flushed.lock().unwrap(), [(1, 1)]);
572 }
573
574 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
576 async fn test_in_use_entries_not_evicted() {
577 let backend = Arc::new(RecordingBackend::default());
578 let cache = AsyncLruCache::new(Arc::clone(&backend), 2);
579
580 let held = cache.get_or_insert(0, false).await.unwrap().unwrap();
581 cache.get_or_insert(1, false).await.unwrap().unwrap();
582
583 let entry = cache.get_or_insert(2, false).await.unwrap();
585 assert_eq!(entry, None);
586 cache.get_or_insert(2, true).await.unwrap().unwrap();
587
588 assert_eq!(*backend.flushed.lock().unwrap(), [(1, 1)]);
589 assert_eq!(*held, 0);
590 }
591
592 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
594 async fn test_cache_full_all_in_use() {
595 const ENTRIES: usize = 23;
596
597 let cache = AsyncLruCache::new(DummyBackend, ENTRIES);
598
599 let mut held = vec![];
600 for i in 0..ENTRIES {
601 held.push(cache.get_or_insert(i, false).await.unwrap().unwrap());
602 }
603
604 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
605 assert_eq!(entry, None);
606 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
607 assert!(err.to_string().contains("everything is in use"));
608 }
609
610 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
612 async fn test_invalidate_retains_in_use() {
613 let cache = AsyncLruCache::new(DummyBackend, 16);
614
615 let held = cache.get_or_insert(0, false).await.unwrap().unwrap();
616 cache.get_or_insert(1, false).await.unwrap().unwrap();
617 cache.get_or_insert(2, false).await.unwrap().unwrap();
618
619 let err = unsafe { cache.invalidate() }.await.unwrap_err();
620 assert!(err.to_string().contains("still in use"));
621
622 let from_cache = cache.get_or_insert(0, false).await.unwrap().unwrap();
623 assert!(Arc::ptr_eq(&from_cache, &held));
624 let from_cache = cache.get_or_insert(0, true).await.unwrap().unwrap();
625 assert!(Arc::ptr_eq(&from_cache, &held));
626
627 let len = cache.0.map.read().await.len();
628 assert_eq!(len, 1);
629 }
630
631 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
633 async fn test_eviction_flush_failure_reinserts_entry() {
634 struct FailFlushBackend;
635
636 #[maybe_async(AFIT)]
637 impl AsyncLruCacheBackend for FailFlushBackend {
638 type Key = usize;
639 type Value = usize;
640
641 async fn load(&self, key: usize) -> io::Result<usize> {
642 Ok(key)
643 }
644
645 async fn flush(&self, _key: usize, _value: &usize) -> io::Result<()> {
646 Err(io::Error::other("flush failed"))
647 }
648
649 unsafe fn evict(&self, _key: usize, _value: usize) {}
650 }
651
652 const ENTRIES: usize = 2;
653
654 let cache = AsyncLruCache::new(FailFlushBackend, ENTRIES);
655
656 for i in 0..ENTRIES {
657 cache.get_or_insert(i, false).await.unwrap().unwrap();
658 }
659
660 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
662 assert_eq!(entry, None);
663 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
665 assert!(err.to_string().contains("flush failed"));
666
667 let len = cache.0.map.read().await.len();
669 assert_eq!(len, ENTRIES);
670 for i in 0..ENTRIES {
671 let entry = cache.get_or_insert(i, false).await.unwrap().unwrap();
672 assert_eq!(*entry, i);
673 }
674
675 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
677 assert_eq!(entry, None);
678 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
679 assert!(err.to_string().contains("flush failed"));
680 }
681
682 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
684 async fn test_insert_flushes_existing() {
685 let backend = Arc::new(RecordingBackend::default());
686 let cache = AsyncLruCache::new(Arc::clone(&backend), 16);
687
688 cache.get_or_insert(5, false).await.unwrap().unwrap();
689 let inserted = cache.insert(5, Arc::new(55), false).await.unwrap();
690 assert!(!inserted);
691 let inserted = cache.insert(5, Arc::new(55), true).await.unwrap();
692 assert!(inserted);
693
694 assert_eq!(*backend.flushed.lock().unwrap(), [(5, 5)]);
695 let entry = *cache.get_or_insert(5, false).await.unwrap().unwrap();
696 assert_eq!(entry, 55);
697 let entry = *cache.get_or_insert(5, true).await.unwrap().unwrap();
698 assert_eq!(entry, 55);
699 let len = cache.0.map.read().await.len();
700 assert_eq!(len, 1);
701 }
702}