auralis_task/
scheduler.rs1use std::cell::RefCell;
9use std::rc::Rc;
10
11use crate::ScheduleFlush;
12
13pub struct DeferredScheduler {
36 pending: RefCell<Vec<Box<dyn FnOnce()>>>,
37}
38
39impl DeferredScheduler {
40 #[must_use]
42 pub fn new() -> Rc<Self> {
43 Rc::new(Self {
44 pending: RefCell::new(Vec::new()),
45 })
46 }
47
48 pub fn drain(&self) {
54 let cbs: Vec<Box<dyn FnOnce()>> = self.pending.borrow_mut().drain(..).collect();
55 for cb in cbs {
56 cb();
57 }
58 }
59
60 #[must_use]
62 pub fn pending_count(&self) -> usize {
63 self.pending.borrow().len()
64 }
65}
66
67impl Default for DeferredScheduler {
68 fn default() -> Self {
69 Self {
70 pending: RefCell::new(Vec::new()),
71 }
72 }
73}
74
75impl ScheduleFlush for DeferredScheduler {
76 fn schedule(&self, callback: Box<dyn FnOnce()>) {
77 self.pending.borrow_mut().push(callback);
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use std::cell::Cell;
84 use std::rc::Rc;
85
86 use super::*;
87
88 #[test]
89 fn deferred_scheduler_buffers_and_drains() {
90 let sched = DeferredScheduler::new();
91 let calls = Rc::new(Cell::new(0u32));
92
93 let c1 = calls.clone();
94 sched.schedule(Box::new(move || c1.set(c1.get() + 1)));
95 let c2 = calls.clone();
96 sched.schedule(Box::new(move || c2.set(c2.get() + 2)));
97
98 assert_eq!(sched.pending_count(), 2);
99 assert_eq!(calls.get(), 0);
100
101 sched.drain();
102 assert_eq!(calls.get(), 3);
103 assert_eq!(sched.pending_count(), 0);
104 }
105
106 #[test]
107 fn drain_empty_is_noop() {
108 let sched = DeferredScheduler::new();
109 sched.drain(); assert_eq!(sched.pending_count(), 0);
111 }
112
113 #[test]
114 fn default_scheduler_has_no_pending() {
115 let sched = DeferredScheduler::default();
116 assert_eq!(sched.pending_count(), 0);
117 }
118
119 #[test]
120 fn reentrant_schedule_during_drain_deferred_to_next_drain() {
121 let sched = DeferredScheduler::new();
124 let calls = Rc::new(Cell::new(0u32));
125
126 let sched_weak = Rc::downgrade(&sched);
129 let c1 = calls.clone();
130 sched.schedule(Box::new(move || {
131 c1.set(1);
132 if let Some(s) = sched_weak.upgrade() {
134 s.schedule(Box::new(|| {}));
135 }
136 }));
137
138 assert_eq!(sched.pending_count(), 1);
139 sched.drain();
140 assert_eq!(calls.get(), 1);
141 assert_eq!(
144 sched.pending_count(),
145 1,
146 "re-entrant callback should be pending"
147 );
148
149 sched.drain();
150 assert_eq!(sched.pending_count(), 0);
151 }
152}