use std::collections::HashMap; 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 } }