1use yog_sync::{Arc, Mutex, Once};
5
6use yog_abi::priority;
7use yog_event::server::game::Tick;
8
9use crate::registry::Registry;
10
11type Task = Arc<dyn Fn() + Send + Sync>;
13
14struct Tasks<T> {
16 once: Vec<(u64, T)>,
18 repeating: Vec<(u64, u64, T)>,
20}
21
22impl<T: Clone> Tasks<T> {
23 const fn new() -> Self {
24 Self {
25 once: Vec::new(),
26 repeating: Vec::new(),
27 }
28 }
29
30 fn due(&mut self) -> Vec<T> {
35 let mut out = Vec::new();
36 self.once.retain_mut(|(left, task)| {
37 if *left <= 1 {
38 out.push(task.clone());
39 false
40 } else {
41 *left -= 1;
42 true
43 }
44 });
45 for (period, left, task) in &mut self.repeating {
46 *left = left.saturating_sub(1);
47 if *left == 0 {
48 out.push(task.clone());
49 *left = (*period).max(1);
50 }
51 }
52 out
53 }
54}
55
56static TASKS: Mutex<Tasks<Task>> = Mutex::new(Tasks::new());
57static TICKING: Once = Once::new();
58
59impl Registry {
60 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
62 where
63 F: Fn() + Send + Sync + 'static,
64 {
65 TASKS.lock().once.push((delay_ticks, Arc::new(handler)));
66 self.tick_tasks();
67 }
68
69 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
71 where
72 F: Fn() + Send + Sync + 'static,
73 {
74 TASKS
75 .lock()
76 .repeating
77 .push((period_ticks, period_ticks.max(1), Arc::new(handler)));
78 self.tick_tasks();
79 }
80
81 fn tick_tasks(&self) {
83 TICKING.call_once(|| {
84 self.after_at::<Tick, _, _>(priority::LATE, |_, _| {
85 let due = TASKS.lock().due();
86 for task in due {
87 crate::registry::guard((), &*task);
88 }
89 });
90 });
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn scheduled_tasks_run_when_due() {
100 let mut tasks = Tasks::new();
101 tasks.once.extend([(0, 'a'), (3, 'b')]);
102 tasks.repeating.extend([(2, 2, 'c'), (0, 1, 'd')]);
103 let per_tick: Vec<usize> = (0..6).map(|_| tasks.due().len()).collect();
104 assert_eq!(per_tick, [2, 2, 2, 2, 1, 2]);
106 assert!(tasks.once.is_empty(), "once tasks are gone once run");
107 }
108}