use std::time::Duration;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
pub struct FsmTimer {
handle: JoinHandle<()>,
cancel: mpsc::Sender<()>,
}
impl FsmTimer {
pub fn start(interval: Duration, tick_sender: mpsc::Sender<TimerTick>) -> Self {
let (cancel_tx, mut cancel_rx) = mpsc::channel::<()>(1);
let handle = tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(interval);
interval_timer.tick().await;
loop {
tokio::select! {
_ = interval_timer.tick() => {
if tick_sender.send(TimerTick).await.is_err() {
break;
}
}
_ = cancel_rx.recv() => {
break;
}
}
}
});
Self {
handle,
cancel: cancel_tx,
}
}
pub async fn stop(self) {
let _ = self.cancel.send(()).await;
let _ = self.handle.await;
}
pub fn cancel(&self) {
let cancel = self.cancel.clone();
tokio::spawn(async move {
let _ = cancel.send(()).await;
});
}
}
#[derive(Debug, Clone)]
pub struct TimerTick;
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_timer_fires() {
let (tx, mut rx) = mpsc::channel(10);
let _timer = FsmTimer::start(Duration::from_millis(50), tx);
let tick = tokio::time::timeout(Duration::from_millis(200), rx.recv()).await;
assert!(tick.is_ok());
}
#[tokio::test]
async fn test_timer_stop() {
let (tx, mut rx) = mpsc::channel(10);
let timer = FsmTimer::start(Duration::from_millis(50), tx);
timer.stop().await;
tokio::time::sleep(Duration::from_millis(100)).await;
let result = rx.try_recv();
assert!(result.is_err());
}
}