use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, MutexGuard, OnceLock};
use crate::encoder::WorkerCaches;
static NEXT_GENERATION: AtomicU64 = AtomicU64::new(1);
pub(crate) fn next_generation() -> u64 {
NEXT_GENERATION.fetch_add(1, Ordering::Relaxed)
}
struct Slot {
caches: Option<WorkerCaches>,
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 { caches: None, generation: 0 }))
.collect()
})
}
pub(crate) struct CacheLease {
guard: Option<MutexGuard<'static, Slot>>,
private: Option<WorkerCaches>,
}
impl CacheLease {
pub(crate) fn checkout(generation: u64) -> Self {
for slot in slots() {
if let Ok(guard) = slot.try_lock() {
if guard.generation == generation && guard.caches.is_some() {
return Self { guard: Some(guard), private: None };
}
}
}
let mut stale_guard = None;
for slot in slots() {
if let Ok(mut guard) = slot.try_lock() {
if guard.caches.is_none() {
guard.caches = Some(WorkerCaches::new());
guard.generation = generation;
return Self { guard: Some(guard), private: None };
}
if stale_guard.is_none() {
stale_guard = Some(guard);
}
}
}
if let Some(mut guard) = stale_guard {
guard.caches.as_mut().unwrap().clear();
guard.generation = generation;
return Self { guard: Some(guard), private: None };
}
Self { guard: None, private: Some(WorkerCaches::new()) }
}
#[inline]
pub(crate) fn caches(&mut self) -> &mut WorkerCaches {
match self.guard {
Some(ref mut g) => g.caches.as_mut().unwrap(),
None => self.private.as_mut().unwrap(),
}
}
}