use futures::lock::Mutex as FuturesMutex;
use std::any::Any;
use std::collections::HashMap;
use std::future::Future;
use std::sync::{Arc, OnceLock, RwLock};
pub(crate) type AnyService = Arc<dyn Any + Send + Sync>;
pub(crate) struct LazyCache {
cells: RwLock<HashMap<usize, Arc<OnceLock<AnyService>>>>,
async_locks: RwLock<HashMap<usize, Arc<FuturesMutex<Option<AnyService>>>>>,
}
impl LazyCache {
pub fn new() -> Self {
Self {
cells: RwLock::new(HashMap::new()),
async_locks: RwLock::new(HashMap::new()),
}
}
pub fn get(&self, key: usize) -> Option<AnyService> {
self.cells
.read()
.unwrap()
.get(&key)
.and_then(|cell| cell.get().cloned())
}
pub fn prefill(&self, key: usize, instance: AnyService) {
let cell = {
let mut cells = self.cells.write().unwrap();
cells
.entry(key)
.or_insert_with(|| Arc::new(OnceLock::new()))
.clone()
};
let _ = cell.set(instance);
}
pub fn get_or_init_with<F>(&self, key: usize, factory: F) -> AnyService
where
F: FnOnce() -> AnyService,
{
{
let cells = self.cells.read().unwrap();
if let Some(cell) = cells.get(&key) {
if let Some(instance) = cell.get() {
return instance.clone();
}
let cell = cell.clone();
drop(cells);
return cell.get_or_init(factory).clone();
}
}
let cell = {
let mut cells = self.cells.write().unwrap();
cells
.entry(key)
.or_insert_with(|| Arc::new(OnceLock::new()))
.clone()
};
cell.get_or_init(factory).clone()
}
pub async fn get_or_init_with_async<F, Fut, E>(
&self,
key: usize,
factory: F,
) -> Result<AnyService, E>
where
F: FnOnce() -> Fut + Send,
Fut: Future<Output = Result<AnyService, E>> + Send,
E: Send,
{
if let Some(cached) = self.get(key) {
return Ok(cached);
}
let lock = {
let mut locks = self.async_locks.write().unwrap();
locks
.entry(key)
.or_insert_with(|| Arc::new(FuturesMutex::new(None)))
.clone()
};
let mut guard = lock.lock().await;
if let Some(ref instance) = *guard {
return Ok(instance.clone());
}
if let Some(cached) = self.get(key) {
*guard = Some(cached.clone());
return Ok(cached);
}
let instance = factory().await?;
*guard = Some(instance.clone());
self.prefill(key, instance.clone());
Ok(instance)
}
}
impl Default for LazyCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
#[test]
fn get_or_init_executes_factory_once_under_concurrency() {
static CALLS: AtomicU64 = AtomicU64::new(0);
let cache = LazyCache::new();
let cache = Arc::new(cache);
let mut handles = Vec::new();
for _ in 0..8 {
let c = cache.clone();
handles.push(thread::spawn(move || {
c.get_or_init_with(42, || {
CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(99_u64) as AnyService
})
}));
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(CALLS.load(Ordering::SeqCst), 1, "工厂应只执行一次");
for r in &results {
let v = r.downcast_ref::<u64>().unwrap();
assert_eq!(*v, 99);
}
}
#[test]
fn prefill_skips_reinit() {
let cache = LazyCache::new();
cache.prefill(1, Arc::new(7_u64) as AnyService);
cache.prefill(1, Arc::new(999_u64) as AnyService);
let instance = cache.get(1).unwrap();
let v = instance.downcast_ref::<u64>().unwrap();
assert_eq!(*v, 7);
}
#[test]
fn get_returns_none_for_missing() {
let cache = LazyCache::new();
assert!(cache.get(0).is_none());
}
}