use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use tokio_util::sync::CancellationToken;
type TaskHandler = dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync;
pub struct ScheduledTask {
pub name: &'static str,
pub interval: Duration,
handler: Arc<TaskHandler>,
pub enabled: bool,
}
pub struct WorkerScheduler {
tasks: Vec<ScheduledTask>,
cancel: CancellationToken,
}
impl WorkerScheduler {
pub fn new(cancel: CancellationToken) -> Self {
Self {
tasks: Vec::new(),
cancel,
}
}
pub fn register(
&mut self,
name: &'static str,
interval: Duration,
handler: impl Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static,
) {
self.tasks.push(ScheduledTask {
name,
interval,
handler: Arc::new(handler),
enabled: true,
});
}
pub fn register_optional(
&mut self,
name: &'static str,
interval: Duration,
enabled: bool,
handler: impl Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static,
) {
self.tasks.push(ScheduledTask {
name,
interval,
handler: Arc::new(handler),
enabled,
});
}
pub fn task_count(&self) -> usize {
self.tasks.len()
}
pub fn enabled_count(&self) -> usize {
self.tasks.iter().filter(|t| t.enabled).count()
}
pub fn task_names(&self) -> Vec<&'static str> {
self.tasks.iter().map(|t| t.name).collect()
}
pub async fn run(&self) {
let mut handles = Vec::new();
for task in &self.tasks {
if !task.enabled {
continue;
}
let cancel = self.cancel.clone();
let interval = task.interval;
let handler = Arc::clone(&task.handler);
let handle = tokio::spawn(async move {
loop {
tokio::select! {
_ = cancel.cancelled() => {
break;
}
_ = tokio::time::sleep(interval) => {
handler().await;
}
}
}
});
handles.push(handle);
}
if handles.is_empty() {
self.cancel.cancelled().await;
return;
}
for handle in handles {
let _ = handle.await;
}
}
pub fn shutdown(&self) {
self.cancel.cancel();
}
}
#[cfg(test)]
#[path = "tests/scheduler_tests.rs"]
mod tests;