use fibre_cache::CacheBuilder;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Barrier,
};
use std::thread;
use std::time::Duration;
#[test]
fn test_sync_concurrent_load_and_invalidate() {
let cache = Arc::new(
CacheBuilder::default()
.capacity(10)
.loader(|key: i32| {
thread::sleep(Duration::from_millis(50));
(key * 10, 1)
})
.build()
.unwrap(),
);
let num_loaders = 5;
let barrier = Arc::new(Barrier::new(num_loaders + 1));
let mut handles = vec![];
for _ in 0..num_loaders {
let cache_clone = cache.clone();
let barrier_clone = barrier.clone();
handles.push(thread::spawn(move || {
barrier_clone.wait();
let value = cache_clone.fetch_with(&1);
assert_eq!(*value, 10);
}));
}
let cache_clone = cache.clone();
let barrier_clone = barrier.clone();
handles.push(thread::spawn(move || {
barrier_clone.wait();
let _was_present = cache_clone.invalidate(&1);
}));
for handle in handles {
handle.join().unwrap(); }
let metrics = cache.metrics();
assert!(metrics.inserts >= 1); assert!(metrics.invalidations <= 1);
}
#[test]
fn test_sync_concurrent_insert_and_clear() {
let cache = Arc::new(
CacheBuilder::<i32, i32>::default()
.capacity(1_000_000)
.build()
.unwrap(),
);
let stop_inserting = Arc::new(AtomicBool::new(false));
let cache_clone = cache.clone();
let stop_clone = stop_inserting.clone();
let insert_handle = thread::spawn(move || {
for i in 0.. {
if stop_clone.load(Ordering::Relaxed) {
break;
}
cache_clone.insert(i, i, 1);
}
});
let cache_clone_2 = cache.clone();
let stop_clone_2 = stop_inserting.clone();
let clear_handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(20));
cache_clone_2.clear();
stop_clone_2.store(true, Ordering::Relaxed);
});
insert_handle.join().unwrap();
clear_handle.join().unwrap();
let final_cost = cache.metrics().current_cost;
assert!(
final_cost < 100,
"Final cost should be very low after clear, but was {}",
final_cost
);
}