use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, MutexGuard, OnceLock};
use crate::encoder::PretokenCache;
static NEXT_GENERATION: AtomicU64 = AtomicU64::new(1);
pub(crate) fn next_generation() -> u64 {
NEXT_GENERATION.fetch_add(1, Ordering::Relaxed)
}
struct Slot {
cache: Option<PretokenCache>,
generation: u64,
}
fn slots() -> &'static [Mutex<Slot>] {
static SLOTS: OnceLock<Vec<Mutex<Slot>>> = OnceLock::new();
SLOTS.get_or_init(|| {
let n = std::thread::available_parallelism().map(|p| p.get()).unwrap_or(1);
(0..n)
.map(|_| Mutex::new(Slot { cache: None, generation: 0 }))
.collect()
})
}
pub(crate) struct CacheLease {
guard: Option<MutexGuard<'static, Slot>>,
private: Option<PretokenCache>,
}
impl CacheLease {
pub(crate) fn checkout(generation: u64) -> Self {
for slot in slots() {
if let Ok(mut guard) = slot.try_lock() {
let stale = guard.generation != generation;
match guard.cache {
Some(ref mut c) if stale => c.clear(),
None => guard.cache = Some(PretokenCache::new()),
_ => {}
}
guard.generation = generation;
return Self { guard: Some(guard), private: None };
}
}
Self { guard: None, private: Some(PretokenCache::new()) }
}
#[inline]
pub(crate) fn cache(&mut self) -> &mut PretokenCache {
match self.guard {
Some(ref mut g) => g.cache.as_mut().unwrap(),
None => self.private.as_mut().unwrap(),
}
}
}