use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{watch, Mutex as TokioMutex};
use tokio::time::interval;
pub struct AsyncLockWatchdog {
stop_tx: Arc<TokioMutex<Option<watch::Sender<()>>>>,
task_handle: TokioMutex<Option<tokio::task::JoinHandle<()>>>,
}
impl AsyncLockWatchdog {
pub fn new() -> Self {
Self {
stop_tx: Arc::new(TokioMutex::new(None)),
task_handle: TokioMutex::new(None),
}
}
pub async fn start<F, Fut>(&mut self, renew_interval: Duration, renew_func: F)
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = bool> + Send + 'static,
{
self.stop().await;
let (stop_tx, mut stop_rx) = watch::channel(());
*self.stop_tx.lock().await = Some(stop_tx);
let renew_func = Arc::new(renew_func);
let handle = tokio::spawn({
let renew_func = renew_func.clone();
async move {
let mut interval = interval(renew_interval);
if !renew_func().await {
return;
}
loop {
tokio::select! {
_ = interval.tick() => {
if !renew_func().await {
break;
}
}
_ = stop_rx.changed() => {
break;
}
}
}
}
});
*self.task_handle.lock().await = Some(handle);
}
pub async fn stop(&mut self) {
if let Some(stop_tx) = self.stop_tx.lock().await.take() {
let _ = stop_tx.send(());
}
if let Some(handle) = self.task_handle.lock().await.take() {
let _ = handle.await;
}
}
}