1 #[cfg(test)] 2 mod periodic_timer_test; 3 4 use tokio::sync::{mpsc, Mutex}; 5 use tokio::time::Duration; 6 7 use std::sync::Arc; 8 9 use async_trait::async_trait; 10 11 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 12 pub enum TimerIdRefresh { 13 Alloc, 14 Perms, 15 } 16 17 impl Default for TimerIdRefresh { 18 fn default() -> Self { 19 TimerIdRefresh::Alloc 20 } 21 } 22 23 // PeriodicTimerTimeoutHandler is a handler called on timeout 24 #[async_trait] 25 pub trait PeriodicTimerTimeoutHandler { 26 async fn on_timeout(&mut self, id: TimerIdRefresh); 27 } 28 29 // PeriodicTimer is a periodic timer 30 #[derive(Default)] 31 pub struct PeriodicTimer { 32 id: TimerIdRefresh, 33 interval: Duration, 34 close_tx: Mutex<Option<mpsc::Sender<()>>>, 35 } 36 37 impl PeriodicTimer { 38 // create a new timer 39 pub fn new(id: TimerIdRefresh, interval: Duration) -> Self { 40 PeriodicTimer { 41 id, 42 interval, 43 close_tx: Mutex::new(None), 44 } 45 } 46 47 // Start starts the timer. 48 pub async fn start<T: 'static + PeriodicTimerTimeoutHandler + std::marker::Send>( 49 &self, 50 timeout_handler: Arc<Mutex<T>>, 51 ) -> bool { 52 // this is a noop if the timer is always running 53 { 54 let close_tx = self.close_tx.lock().await; 55 if close_tx.is_some() { 56 return false; 57 } 58 } 59 60 let (close_tx, mut close_rx) = mpsc::channel(1); 61 let interval = self.interval; 62 let id = self.id; 63 64 tokio::spawn(async move { 65 loop { 66 let timer = tokio::time::sleep(interval); 67 tokio::pin!(timer); 68 69 tokio::select! { 70 _ = timer.as_mut() => { 71 let mut handler = timeout_handler.lock().await; 72 handler.on_timeout(id).await; 73 } 74 _ = close_rx.recv() => break, 75 } 76 } 77 }); 78 79 { 80 let mut close = self.close_tx.lock().await; 81 *close = Some(close_tx); 82 } 83 84 true 85 } 86 87 // Stop stops the timer. 88 pub async fn stop(&self) { 89 let mut close_tx = self.close_tx.lock().await; 90 close_tx.take(); 91 } 92 93 // is_running tests if the timer is running. 94 // Debug purpose only 95 pub async fn is_running(&self) -> bool { 96 let close_tx = self.close_tx.lock().await; 97 close_tx.is_some() 98 } 99 } 100