use std::sync::{
atomic::{AtomicBool, Ordering},
LazyLock, RwLock,
};
use moka::sync::Cache;
use crate::{simulator::likelihood::SubjectPredictions, PharmsolError};
pub const DEFAULT_CACHE_SIZE: u64 = 100_000;
static CACHE_ENABLED: AtomicBool = AtomicBool::new(true);
#[derive(Debug, Clone)]
pub struct CacheSettings {
pub enabled: bool,
pub size: u64,
}
impl Default for CacheSettings {
fn default() -> Self {
Self {
enabled: true,
size: DEFAULT_CACHE_SIZE,
}
}
}
impl CacheSettings {
pub fn disabled() -> Self {
Self {
enabled: false,
..Default::default()
}
}
pub fn with_size(size: u64) -> Self {
Self {
enabled: true,
size,
}
}
}
pub fn configure_cache(settings: CacheSettings) -> Result<(), PharmsolError> {
CACHE_ENABLED.store(settings.enabled, Ordering::Relaxed);
let size = settings.size;
{
let mut c = ana_cache_lock_write()?;
*c = Cache::new(size);
}
{
let mut c = ode_cache_lock_write()?;
*c = Cache::new(size);
}
{
let mut c = sde_cache_lock_write()?;
*c = Cache::new(size);
}
Ok(())
}
pub fn reset_caches() -> Result<(), PharmsolError> {
ana_cache_lock_read()?.invalidate_all();
ode_cache_lock_read()?.invalidate_all();
sde_cache_lock_read()?.invalidate_all();
Ok(())
}
pub fn disable_cache() -> Result<(), PharmsolError> {
CACHE_ENABLED.store(false, Ordering::Relaxed);
reset_caches()
}
pub fn enable_cache() {
CACHE_ENABLED.store(true, Ordering::Relaxed);
}
#[inline(always)]
pub fn cache_enabled() -> bool {
CACHE_ENABLED.load(Ordering::Relaxed)
}
pub fn cache_settings() -> Result<CacheSettings, PharmsolError> {
let size = ana_cache_lock_read()?.policy().max_capacity().unwrap_or(0);
Ok(CacheSettings {
enabled: cache_enabled(),
size,
})
}
pub(crate) type PredictionKey = (u64, u64);
pub(crate) type SdeKey = (u64, u64, u64);
static ANA_CACHE: LazyLock<RwLock<Cache<PredictionKey, SubjectPredictions>>> =
LazyLock::new(|| RwLock::new(Cache::new(DEFAULT_CACHE_SIZE)));
static ODE_CACHE: LazyLock<RwLock<Cache<PredictionKey, SubjectPredictions>>> =
LazyLock::new(|| RwLock::new(Cache::new(DEFAULT_CACHE_SIZE)));
static SDE_CACHE: LazyLock<RwLock<Cache<SdeKey, f64>>> =
LazyLock::new(|| RwLock::new(Cache::new(DEFAULT_CACHE_SIZE)));
fn lock_err(context: &str) -> PharmsolError {
PharmsolError::OtherError(format!("Failed to lock {context} cache"))
}
pub(crate) fn ana_cache_lock_read() -> Result<
std::sync::RwLockReadGuard<'static, Cache<PredictionKey, SubjectPredictions>>,
PharmsolError,
> {
ANA_CACHE.read().map_err(|_| lock_err("analytical"))
}
fn ana_cache_lock_write() -> Result<
std::sync::RwLockWriteGuard<'static, Cache<PredictionKey, SubjectPredictions>>,
PharmsolError,
> {
ANA_CACHE.write().map_err(|_| lock_err("analytical"))
}
pub(crate) fn ode_cache_lock_read() -> Result<
std::sync::RwLockReadGuard<'static, Cache<PredictionKey, SubjectPredictions>>,
PharmsolError,
> {
ODE_CACHE.read().map_err(|_| lock_err("ODE"))
}
fn ode_cache_lock_write() -> Result<
std::sync::RwLockWriteGuard<'static, Cache<PredictionKey, SubjectPredictions>>,
PharmsolError,
> {
ODE_CACHE.write().map_err(|_| lock_err("ODE"))
}
pub(crate) fn sde_cache_lock_read(
) -> Result<std::sync::RwLockReadGuard<'static, Cache<SdeKey, f64>>, PharmsolError> {
SDE_CACHE.read().map_err(|_| lock_err("SDE"))
}
fn sde_cache_lock_write(
) -> Result<std::sync::RwLockWriteGuard<'static, Cache<SdeKey, f64>>, PharmsolError> {
SDE_CACHE.write().map_err(|_| lock_err("SDE"))
}