runtime/event_loop/
macrotasks.rs1use std::collections::HashMap;
8use std::fmt;
9use std::fmt::{Debug, Formatter};
10use std::sync::Arc;
11use std::sync::atomic::{AtomicBool, Ordering};
12use std::time::{Duration, Instant};
13
14use ion::{Context, ErrorReport, Function, Object, Value};
15use mozjs::jsapi::JSFunction;
16use mozjs::jsval::JSVal;
17
18pub struct SignalMacrotask {
19 callback: Option<Box<dyn FnOnce()>>,
20 terminate: Arc<AtomicBool>,
21 scheduled: Instant,
22}
23
24impl SignalMacrotask {
25 pub fn new(callback: Box<dyn FnOnce()>, terminate: Arc<AtomicBool>, duration: Duration) -> SignalMacrotask {
26 SignalMacrotask {
27 callback: Some(callback),
28 terminate,
29 scheduled: Instant::now() + duration,
30 }
31 }
32}
33
34impl Debug for SignalMacrotask {
35 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
36 f.debug_struct("SignalMacrotask")
37 .field("callback", &self.callback.as_ref().map(|_| ()))
38 .field("terminate", &self.terminate.as_ref())
39 .field("scheduled", &self.scheduled)
40 .finish()
41 }
42}
43
44#[derive(Debug)]
45pub struct TimerMacrotask {
46 callback: *mut JSFunction,
47 arguments: Box<[JSVal]>,
48 repeat: bool,
49 scheduled: Instant,
50 duration: Duration,
51 nesting: u8,
52}
53
54impl TimerMacrotask {
55 pub fn new(callback: &Function, arguments: Box<[JSVal]>, repeat: bool, duration: Duration) -> TimerMacrotask {
56 TimerMacrotask {
57 callback: callback.get(),
58 arguments,
59 repeat,
60 duration,
61 scheduled: Instant::now(),
62 nesting: 0,
63 }
64 }
65
66 pub fn reset(&mut self) -> bool {
67 if self.repeat {
68 self.scheduled = Instant::now();
69 }
70 self.repeat
71 }
72}
73
74#[derive(Debug)]
75pub struct UserMacrotask {
76 callback: *mut JSFunction,
77 scheduled: Instant,
78}
79
80impl UserMacrotask {
81 pub fn new(callback: &Function) -> UserMacrotask {
82 UserMacrotask {
83 callback: callback.get(),
84 scheduled: Instant::now(),
85 }
86 }
87}
88
89#[derive(Debug)]
90pub enum Macrotask {
91 Signal(SignalMacrotask),
92 Timer(TimerMacrotask),
93 User(UserMacrotask),
94}
95
96#[derive(Debug, Default)]
97pub struct MacrotaskQueue {
98 pub(crate) map: HashMap<u32, Macrotask>,
99 pub(crate) nesting: u8,
100 next: Option<u32>,
101 latest: Option<u32>,
102}
103
104impl Macrotask {
105 pub fn run(&mut self, cx: &Context) -> Result<(), Option<ErrorReport>> {
106 if let Macrotask::Signal(signal) = self {
107 if let Some(callback) = signal.callback.take() {
108 callback();
109 }
110 return Ok(());
111 }
112
113 let (callback, args) = match self {
114 Macrotask::Timer(timer) => (timer.callback, timer.arguments.clone()),
115 Macrotask::User(user) => (user.callback, Box::default()),
116 _ => unreachable!(),
117 };
118
119 let callback = Function::from(cx.root(callback));
120 let args: Vec<_> = args.into_vec().into_iter().map(|value| Value::from(cx.root(value))).collect();
121
122 callback.call(cx, &Object::global(cx), args.as_slice())?;
123 Ok(())
124 }
125
126 pub fn remove(&mut self) -> bool {
127 match self {
128 Macrotask::Timer(timer) => !timer.reset(),
129 _ => true,
130 }
131 }
132
133 fn terminate(&self) -> bool {
134 match self {
135 Macrotask::Signal(signal) => signal.terminate.load(Ordering::SeqCst),
136 _ => false,
137 }
138 }
139
140 fn remaining(&self) -> Duration {
141 match self {
142 Macrotask::Signal(signal) => signal.scheduled - Instant::now(),
143 Macrotask::Timer(timer) => timer.scheduled + timer.duration - Instant::now(),
144 Macrotask::User(user) => user.scheduled - Instant::now(),
145 }
146 }
147}
148
149impl MacrotaskQueue {
150 pub fn run_job(&mut self, cx: &Context) -> Result<(), Option<ErrorReport>> {
151 self.find_next();
152 if let Some(next) = self.next {
153 {
154 let macrotask = self.map.get_mut(&next);
155 if let Some(macrotask) = macrotask {
156 macrotask.run(cx)?;
157 }
158 }
159
160 let macrotask = self.map.get_mut(&next);
162 if let Some(macrotask) = macrotask
163 && macrotask.remove()
164 {
165 self.map.remove(&next);
166 }
167 }
168
169 Ok(())
170 }
171
172 pub fn enqueue(&mut self, mut macrotask: Macrotask, id: Option<u32>) -> u32 {
173 let index = id.unwrap_or_else(|| self.latest.map_or(0, |l| l + 1));
174
175 let next = self.next.and_then(|next| self.map.get(&next));
176 if let Some(next) = next {
177 if macrotask.remaining() < next.remaining() {
178 self.set_next(index, ¯otask);
179 }
180 } else {
181 self.set_next(index, ¯otask);
182 }
183
184 if let Macrotask::Timer(timer) = &mut macrotask {
185 self.nesting += 1;
186 timer.nesting = self.nesting;
187 }
188
189 self.latest = Some(index);
190 self.map.insert(index, macrotask);
191
192 index
193 }
194
195 pub fn remove(&mut self, id: u32) {
196 if self.map.remove(&id).is_some()
197 && let Some(next) = self.next
198 && next == id
199 {
200 self.next = None;
201 }
202 }
203
204 pub fn find_next(&mut self) {
205 let mut next: Option<(u32, Duration)> = None;
206
207 self.map.retain(|id, macrotask| {
208 if macrotask.terminate() {
209 return false;
210 }
211
212 let duration = macrotask.remaining();
213 if let Some((_, next_duration)) = next {
214 if duration < next_duration {
215 next = Some((*id, duration));
216 }
217 } else if duration <= Duration::ZERO {
218 next = Some((*id, duration));
219 }
220
221 true
222 });
223
224 self.next = next.map(|(id, _)| id);
225 }
226
227 pub fn set_next(&mut self, index: u32, macrotask: &Macrotask) {
228 if macrotask.remaining() <= Duration::ZERO {
229 self.next = Some(index);
230 }
231 }
232
233 pub fn is_empty(&self) -> bool {
234 self.map.is_empty()
235 }
236}