use std::{ ptr, sync::{atomic::{AtomicPtr, AtomicU64, Ordering}, Arc, Mutex}, time::{Duration, SystemTime, UNIX_EPOCH}, }; use tokio::spawn; use crate::error::{Error, Result}; type UpdateFn = Box Result>; #[derive(Clone, Debug, Default)] pub struct Opts { return_last_good: bool, no_wait: bool, } pub struct Cache { update_fn: UpdateFn, ttl: Duration, opts: Opts, val: AtomicPtr, last_update_ms: AtomicU64, updating: Arc>, } impl Cache { pub fn new(update_fn: UpdateFn, ttl: Duration, opts: Opts) -> Self { let val = AtomicPtr::new(ptr::null_mut()); Self { update_fn, ttl, opts, val, last_update_ms: AtomicU64::new(0), updating: Arc::new(Mutex::new(false)), } } pub fn get(&self) -> Result { let v_ptr = self.val.load(Ordering::SeqCst); let v = if v_ptr.is_null() { None } else { Some(unsafe { *v_ptr }) }; let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") .as_secs(); if v.is_some() && now - self.last_update_ms.load(Ordering::SeqCst) < self.ttl.as_secs() { return Ok(v.unwrap()); } if self.opts.no_wait && v.is_some() && now - self.last_update_ms.load(Ordering::SeqCst) < self.ttl.as_secs() * 2 { if self.updating.try_lock().is_ok() { let this = self.clone(); spawn(async { let _ = this.update().await; }); self.update(); } return Ok(v.unwrap()); } match self.updating.lock() { Ok(_) => { if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH + Duration::from_secs(self.last_update_ms.load(Ordering::SeqCst))) { if duration < self.ttl { return Ok(v.unwrap()); } } match self.update() { } }, Err(err) => { return Err(Error::from_string(err.to_string())); } } todo!() } async fn update(&self) -> Result<()> { todo!() } }