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 trace!("Flushing {oldest_key:?}");
236 let oldest_entry = map.get(&oldest_key).unwrap();
237 self.backend.flush(oldest_key, oldest_entry.value()).await?;
238 map.remove(&oldest_key);
239 }
240
241 Ok(true)
242 }
243
244 async fn get_or_insert(&self, key: K, may_flush: bool) -> io::Result<Option<Arc<V>>> {
256 {
257 let map = self.map.read().await;
258 if let Some(entry) = map.get(&key) {
259 entry.last_used.store(
260 self.lru_timer.fetch_add(1, Ordering::Relaxed),
261 Ordering::Relaxed,
262 );
263 return Ok(Some(Arc::clone(entry.value())));
264 }
265 }
266
267 let mut map = self.map.write().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 if !self.ensure_free_entry(&mut map, may_flush).await? {
277 return Ok(None);
278 }
279
280 let object = Arc::new(self.backend.load(key).await?);
281
282 let new_entry = AsyncLruCacheEntry {
283 value: Some(Arc::clone(&object)),
284 last_used: AtomicUsize::new(self.lru_timer.fetch_add(1, Ordering::Relaxed)),
285 };
286 map.insert(key, new_entry);
287
288 Ok(Some(object))
289 }
290
291 async fn insert(&self, key: K, value: Arc<V>, may_flush: bool) -> io::Result<bool> {
302 let mut map = self.map.write().await;
303 if let Some(entry) = map.get_mut(&key) {
304 if !may_flush {
305 return Ok(false);
306 }
307
308 entry.last_used.store(
309 self.lru_timer.fetch_add(1, Ordering::Relaxed),
310 Ordering::Relaxed,
311 );
312 self.backend.flush(key, entry.value()).await?;
313 entry.value = Some(value);
314 } else {
315 if !self.ensure_free_entry(&mut map, may_flush).await? {
316 return Ok(false);
317 }
318
319 let new_entry = AsyncLruCacheEntry {
320 value: Some(value),
321 last_used: AtomicUsize::new(self.lru_timer.fetch_add(1, Ordering::Relaxed)),
322 };
323 map.insert(key, new_entry);
324 }
325
326 Ok(true)
327 }
328
329 #[instrument(
333 level = "trace",
334 name = "AsyncLruCache::flush",
335 skip_all,
336 fields(self = &self as *const _ as usize)
337 )]
338 async fn flush(&self) -> io::Result<()> {
339 #[cfg(feature = "async")]
340 let mut futs = FuturesUnordered::new();
341
342 let map = self.map.read().await;
343 let mut first_err: Option<io::Error> = None;
344 for (key, entry) in map.iter() {
345 let key = *key;
346 trace!("Flushing {key:?}");
347 #[cfg(feature = "async")]
348 futs.push({
349 let object = Arc::clone(entry.value());
350 async move { self.backend.flush(key, &object).await }
351 });
352 #[cfg(feature = "sync")]
353 if let Err(e) = self.backend.flush(key, entry.value()) {
354 first_err.get_or_insert(e);
355 }
356 }
357
358 #[cfg(feature = "async")]
359 while let Some(result) = futs.next().await {
360 if let Err(e) = result {
361 first_err.get_or_insert(e);
362 }
363 }
364 if let Some(e) = first_err {
365 Err(e)
366 } else {
367 Ok(())
368 }
369 }
370
371 #[instrument(
379 level = "trace",
380 name = "AsyncLruCache::invalidate",
381 skip_all,
382 fields(self = &self as *const _ as usize)
383 )]
384 async unsafe fn invalidate(&self) -> io::Result<()> {
385 let mut in_use = Vec::new();
386
387 let mut map = self.map.write().await;
388 let old_map = mem::take(&mut *map);
391 for (key, mut entry) in old_map {
392 let object = entry.value.take().unwrap();
393 trace!("Evicting {key:?}");
394 match Arc::try_unwrap(object) {
395 Ok(object) => {
396 unsafe { self.backend.evict(key, object) };
398 }
399
400 Err(arc) => {
401 trace!("Entry is still in use, retaining it");
402 entry.value = Some(arc);
403 map.insert(key, entry);
404 in_use.push(key);
405 }
406 }
407 }
408
409 if in_use.is_empty() {
410 Ok(())
411 } else {
412 Err(io::Error::other(format!(
413 "Cannot invalidate cache, entries still in use: {}",
414 in_use
415 .iter()
416 .map(|key| format!("{key:?}"))
417 .collect::<Vec<String>>()
418 .join(", "),
419 )))
420 }
421 }
422}
423
424impl<V> AsyncLruCacheEntry<V> {
425 fn value(&self) -> &Arc<V> {
427 self.value.as_ref().unwrap()
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434 use std::sync::atomic::AtomicUsize;
435
436 struct DummyBackend;
438
439 #[maybe_async(AFIT)]
440 impl AsyncLruCacheBackend for DummyBackend {
441 type Key = usize;
442 type Value = usize;
443
444 async fn load(&self, key: usize) -> io::Result<usize> {
445 Ok(key)
446 }
447
448 async fn flush(&self, _key: usize, _value: &usize) -> io::Result<()> {
449 Ok(())
450 }
451
452 unsafe fn evict(&self, _key: usize, _value: usize) {}
453 }
454
455 #[derive(Default)]
457 struct RecordingBackend {
458 flushed: parking_lot::Mutex<Vec<(usize, usize)>>,
459 }
460
461 #[maybe_async(AFIT)]
462 impl AsyncLruCacheBackend for RecordingBackend {
463 type Key = usize;
464 type Value = usize;
465
466 async fn load(&self, key: usize) -> io::Result<usize> {
467 Ok(key)
468 }
469
470 async fn flush(&self, key: usize, value: &usize) -> io::Result<()> {
471 self.flushed.lock().push((key, *value));
472 Ok(())
473 }
474
475 unsafe fn evict(&self, _key: usize, _value: usize) {}
476 }
477
478 #[maybe_async(AFIT)]
479 impl<B: AsyncLruCacheBackend> AsyncLruCacheBackend for Arc<B> {
480 type Key = <B as AsyncLruCacheBackend>::Key;
481 type Value = <B as AsyncLruCacheBackend>::Value;
482
483 async fn load(&self, key: Self::Key) -> io::Result<Self::Value> {
484 (**self).load(key).await
485 }
486
487 async fn flush(&self, key: Self::Key, value: &Self::Value) -> io::Result<()> {
488 (**self).flush(key, value).await
489 }
490
491 unsafe fn evict(&self, key: Self::Key, value: Self::Value) {
492 unsafe { (**self).evict(key, value) }
493 }
494 }
495
496 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
499 async fn test_flush_continues_past_errors() {
500 #[derive(Default)]
501 struct FailOddBackend {
502 flush_count: AtomicUsize,
503 }
504
505 #[maybe_async(AFIT)]
506 impl AsyncLruCacheBackend for FailOddBackend {
507 type Key = usize;
508 type Value = usize;
509
510 async fn load(&self, key: usize) -> io::Result<usize> {
511 Ok(key)
512 }
513
514 async fn flush(&self, key: usize, _value: &usize) -> io::Result<()> {
515 self.flush_count.fetch_add(1, Ordering::Relaxed);
516 if key % 2 == 1 {
517 Err(io::Error::other("odd key"))
518 } else {
519 Ok(())
520 }
521 }
522
523 unsafe fn evict(&self, _key: usize, _value: usize) {}
524 }
525
526 const ENTRIES: usize = 42;
527
528 let backend = Arc::new(FailOddBackend::default());
529 let cache = AsyncLruCache::new(Arc::clone(&backend), ENTRIES);
530
531 for i in 0..ENTRIES {
532 cache.get_or_insert(i, false).await.unwrap().unwrap();
533 }
534
535 let err = cache.flush().await.unwrap_err();
536 assert!(err.to_string().contains("odd key"));
537
538 assert_eq!(backend.flush_count.load(Ordering::Relaxed), ENTRIES);
539 }
540
541 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
543 async fn test_lru_eviction_order() {
544 const ENTRIES: usize = 3;
545
546 let backend = Arc::new(RecordingBackend::default());
547 let cache = AsyncLruCache::new(Arc::clone(&backend), ENTRIES);
548
549 for i in 0..ENTRIES {
550 cache.get_or_insert(i, false).await.unwrap().unwrap();
551 }
552
553 cache.get_or_insert(0, false).await.unwrap().unwrap();
555
556 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
558 assert_eq!(entry, None);
559 cache.get_or_insert(ENTRIES, true).await.unwrap().unwrap();
560
561 assert_eq!(*backend.flushed.lock(), [(1, 1)]);
562 }
563
564 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
566 async fn test_in_use_entries_not_evicted() {
567 let backend = Arc::new(RecordingBackend::default());
568 let cache = AsyncLruCache::new(Arc::clone(&backend), 2);
569
570 let held = cache.get_or_insert(0, false).await.unwrap().unwrap();
571 cache.get_or_insert(1, false).await.unwrap().unwrap();
572
573 let entry = cache.get_or_insert(2, false).await.unwrap();
575 assert_eq!(entry, None);
576 cache.get_or_insert(2, true).await.unwrap().unwrap();
577
578 assert_eq!(*backend.flushed.lock(), [(1, 1)]);
579 assert_eq!(*held, 0);
580 }
581
582 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
584 async fn test_cache_full_all_in_use() {
585 const ENTRIES: usize = 23;
586
587 let cache = AsyncLruCache::new(DummyBackend, ENTRIES);
588
589 let mut held = vec![];
590 for i in 0..ENTRIES {
591 held.push(cache.get_or_insert(i, false).await.unwrap().unwrap());
592 }
593
594 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
595 assert_eq!(entry, None);
596 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
597 assert!(err.to_string().contains("everything is in use"));
598 }
599
600 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
602 async fn test_invalidate_retains_in_use() {
603 let cache = AsyncLruCache::new(DummyBackend, 16);
604
605 let held = cache.get_or_insert(0, false).await.unwrap().unwrap();
606 cache.get_or_insert(1, false).await.unwrap().unwrap();
607 cache.get_or_insert(2, false).await.unwrap().unwrap();
608
609 let err = unsafe { cache.invalidate() }.await.unwrap_err();
610 assert!(err.to_string().contains("still in use"));
611
612 let from_cache = cache.get_or_insert(0, false).await.unwrap().unwrap();
613 assert!(Arc::ptr_eq(&from_cache, &held));
614 let from_cache = cache.get_or_insert(0, true).await.unwrap().unwrap();
615 assert!(Arc::ptr_eq(&from_cache, &held));
616
617 let len = cache.0.map.read().await.len();
618 assert_eq!(len, 1);
619 }
620
621 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
623 async fn test_eviction_flush_failure_reinserts_entry() {
624 struct FailFlushBackend;
625
626 #[maybe_async(AFIT)]
627 impl AsyncLruCacheBackend for FailFlushBackend {
628 type Key = usize;
629 type Value = usize;
630
631 async fn load(&self, key: usize) -> io::Result<usize> {
632 Ok(key)
633 }
634
635 async fn flush(&self, _key: usize, _value: &usize) -> io::Result<()> {
636 Err(io::Error::other("flush failed"))
637 }
638
639 unsafe fn evict(&self, _key: usize, _value: usize) {}
640 }
641
642 const ENTRIES: usize = 2;
643
644 let cache = AsyncLruCache::new(FailFlushBackend, ENTRIES);
645
646 for i in 0..ENTRIES {
647 cache.get_or_insert(i, false).await.unwrap().unwrap();
648 }
649
650 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
652 assert_eq!(entry, None);
653 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
655 assert!(err.to_string().contains("flush failed"));
656
657 let len = cache.0.map.read().await.len();
659 assert_eq!(len, ENTRIES);
660 for i in 0..ENTRIES {
661 let entry = cache.get_or_insert(i, false).await.unwrap().unwrap();
662 assert_eq!(*entry, i);
663 }
664
665 let entry = cache.get_or_insert(ENTRIES, false).await.unwrap();
667 assert_eq!(entry, None);
668 let err = cache.get_or_insert(ENTRIES, true).await.unwrap_err();
669 assert!(err.to_string().contains("flush failed"));
670 }
671
672 #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
674 async fn test_insert_flushes_existing() {
675 let backend = Arc::new(RecordingBackend::default());
676 let cache = AsyncLruCache::new(Arc::clone(&backend), 16);
677
678 cache.get_or_insert(5, false).await.unwrap().unwrap();
679 let inserted = cache.insert(5, Arc::new(55), false).await.unwrap();
680 assert!(!inserted);
681 let inserted = cache.insert(5, Arc::new(55), true).await.unwrap();
682 assert!(inserted);
683
684 assert_eq!(*backend.flushed.lock(), [(5, 5)]);
685 let entry = *cache.get_or_insert(5, false).await.unwrap().unwrap();
686 assert_eq!(entry, 55);
687 let entry = *cache.get_or_insert(5, true).await.unwrap().unwrap();
688 assert_eq!(entry, 55);
689 let len = cache.0.map.read().await.len();
690 assert_eq!(len, 1);
691 }
692
693 #[cfg(feature = "async")]
696 #[tokio::test]
697 async fn test_dropped_eviction_flush_keeps_entry() {
698 use futures::FutureExt;
699
700 #[derive(Default)]
702 struct PendingFlushBackend {
703 loads: AtomicUsize,
704 }
705
706 #[maybe_async(AFIT)]
707 impl AsyncLruCacheBackend for PendingFlushBackend {
708 type Key = usize;
709 type Value = usize;
710
711 async fn load(&self, key: usize) -> io::Result<usize> {
712 self.loads.fetch_add(1, Ordering::Relaxed);
713 Ok(key)
714 }
715
716 async fn flush(&self, _key: usize, _value: &usize) -> io::Result<()> {
717 std::future::pending().await
718 }
719
720 unsafe fn evict(&self, _key: usize, _value: usize) {}
721 }
722
723 let backend = Arc::new(PendingFlushBackend::default());
724 let cache = AsyncLruCache::new(Arc::clone(&backend), 2);
725
726 cache.get_or_insert(0, false).await.unwrap().unwrap();
727 cache.get_or_insert(1, false).await.unwrap().unwrap();
728 assert_eq!(backend.loads.load(Ordering::Relaxed), 2);
729
730 assert!(cache.get_or_insert(2, true).now_or_never().is_none());
732
733 let entry = cache.get_or_insert(0, false).await.unwrap().unwrap();
735 assert_eq!(*entry, 0);
736 let entry = cache.get_or_insert(1, false).await.unwrap().unwrap();
737 assert_eq!(*entry, 1);
738
739 assert_eq!(backend.loads.load(Ordering::Relaxed), 2);
741 }
742}