use std::collections::HashMap; use std::str::FromStr; use std::sync::{Arc, Mutex}; use tokio::time::{self, Duration}; use crate::callback::{CallbackBus, TimerEvent}; struct TimerEntry { handle: tokio::task::JoinHandle<()>, } /// Timer syscall: delayed and periodic execution with callback delivery. pub struct TimerSyscall { timers: Arc>>, next_id: Mutex, bus: Arc, } impl TimerSyscall { pub fn new(bus: Arc) -> Self { Self { timers: Arc::new(Mutex::new(HashMap::new())), next_id: Mutex::new(1), bus, } } /// Schedule a one-shot timer. When it fires, a TimerEvent is sent to the plugin. pub fn once(&self, delay_ms: u64, callback_id: String, plugin_id: String) -> String { let timer_id = { let mut next = self.next_id.lock().unwrap(); let id = format!("timer_{}", *next); *next += 1; id }; let tid = timer_id.clone(); let cb = callback_id.clone(); let pid = plugin_id.clone(); let bus = self.bus.clone(); let timers = self.timers.clone(); let handle = tokio::spawn(async move { time::sleep(Duration::from_millis(delay_ms)).await; tracing::info!("timer fired: id={} callback={} plugin={}", tid, cb, pid); bus.fire( &pid, TimerEvent { timer_id: tid.clone(), callback_id: cb, }, ); timers.lock().unwrap().remove(&tid); }); self.timers .lock() .unwrap() .insert(timer_id.clone(), TimerEntry { handle }); timer_id } pub fn cancel(&self, timer_id: &str) -> Result<(), String> { let mut timers = self.timers.lock().unwrap(); if let Some(entry) = timers.remove(timer_id) { entry.handle.abort(); Ok(()) } else { Err(format!("timer {} not found", timer_id)) } } pub fn now(&self) -> i64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_millis() as i64 } /// Cron 表达式为 cron crate 7 段格式: sec min hour day-of-month month day-of-week year pub fn cron(&self, expr: &str, callback_id: String, plugin_id: String) -> Result { let schedule = cron::Schedule::from_str(expr) .map_err(|e| format!("invalid cron expression: {e}"))?; let timer_id = { let mut next = self.next_id.lock().unwrap(); let id = format!("cron_{}", *next); *next += 1; id }; let tid = timer_id.clone(); let cb = callback_id.clone(); let pid = plugin_id.clone(); let bus = self.bus.clone(); let timers = self.timers.clone(); let handle = tokio::spawn(async move { loop { let next = schedule.upcoming(chrono::Utc).next(); match next { Some(t) => { let dur = (t - chrono::Utc::now()) .to_std() .unwrap_or(std::time::Duration::ZERO); tokio::time::sleep(dur).await; bus.fire( &pid, TimerEvent { timer_id: tid.clone(), callback_id: cb.clone(), }, ); } None => break, } } timers.lock().unwrap().remove(&tid); }); self.timers .lock() .unwrap() .insert(timer_id.clone(), TimerEntry { handle }); Ok(timer_id) } }