use yog_sync::{Arc, Mutex, Once};
use yog_abi::priority;
use yog_event::server::game::Tick;
use crate::registry::Registry;
type Task = Arc<dyn Fn() + Send + Sync>;
struct Tasks<T> {
once: Vec<(u64, T)>,
repeating: Vec<(u64, u64, T)>,
}
impl<T: Clone> Tasks<T> {
const fn new() -> Self {
Self {
once: Vec::new(),
repeating: Vec::new(),
}
}
fn due(&mut self) -> Vec<T> {
let mut out = Vec::new();
self.once.retain_mut(|(left, task)| {
if *left <= 1 {
out.push(task.clone());
false
} else {
*left -= 1;
true
}
});
for (period, left, task) in &mut self.repeating {
*left = left.saturating_sub(1);
if *left == 0 {
out.push(task.clone());
*left = (*period).max(1);
}
}
out
}
}
static TASKS: Mutex<Tasks<Task>> = Mutex::new(Tasks::new());
static TICKING: Once = Once::new();
impl Registry {
pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
where
F: Fn() + Send + Sync + 'static,
{
TASKS.lock().once.push((delay_ticks, Arc::new(handler)));
self.tick_tasks();
}
pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
where
F: Fn() + Send + Sync + 'static,
{
TASKS
.lock()
.repeating
.push((period_ticks, period_ticks.max(1), Arc::new(handler)));
self.tick_tasks();
}
fn tick_tasks(&self) {
TICKING.call_once(|| {
self.after_at::<Tick, _, _>(priority::LATE, |_, _| {
let due = TASKS.lock().due();
for task in due {
crate::registry::guard((), &*task);
}
});
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scheduled_tasks_run_when_due() {
let mut tasks = Tasks::new();
tasks.once.extend([(0, 'a'), (3, 'b')]);
tasks.repeating.extend([(2, 2, 'c'), (0, 1, 'd')]);
let per_tick: Vec<usize> = (0..6).map(|_| tasks.due().len()).collect();
assert_eq!(per_tick, [2, 2, 2, 2, 1, 2]);
assert!(tasks.once.is_empty(), "once tasks are gone once run");
}
}