rqjs_ext/modules/timers.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3use std::{
4 sync::{
5 atomic::{AtomicBool, AtomicUsize, Ordering},
6 Arc, Mutex,
7 },
8 time::{SystemTime, UNIX_EPOCH},
9};
10
11use rquickjs::{
12 module::{Declarations, Exports, ModuleDef},
13 Ctx, Function, Result,
14};
15
16use super::module::export_default;
17
18// use crate::{module_builder::ModuleInfo, modules::module::export_default, vm::CtxExtension};
19
20static TIMER_ID: AtomicUsize = AtomicUsize::new(0);
21static TIME_POLL_ACTIVE: AtomicBool = AtomicBool::new(false);
22
23#[derive(Debug)]
24struct Timeout<'js> {
25 cb: Option<Function<'js>>,
26 timeout: usize,
27 id: usize,
28 repeating: bool,
29 delay: usize,
30}
31
32fn set_immediate(cb: Function) -> Result<()> {
33 cb.defer::<()>(())?;
34 Ok(())
35}
36
37fn get_current_time_millis() -> usize {
38 SystemTime::now()
39 .duration_since(UNIX_EPOCH)
40 .map(|t| t.as_millis() as usize)
41 .unwrap_or(0)
42}
43
44fn set_timeout_interval<'js>(
45 ctx: &Ctx<'js>,
46 timeouts: &Arc<Mutex<Vec<Timeout<'js>>>>,
47 cb: Function<'js>,
48 delay: usize,
49 repeating: bool,
50) -> Result<usize> {
51 let timeout = get_current_time_millis() + delay;
52 let id = TIMER_ID.fetch_add(1, Ordering::Relaxed);
53 timeouts.lock().unwrap().push(Timeout {
54 cb: Some(cb.clone()),
55 timeout,
56 id,
57 repeating,
58 delay,
59 });
60 if !TIME_POLL_ACTIVE.load(Ordering::Relaxed) {
61 // poll_timers(ctx, timeouts.clone())?
62 }
63
64 Ok(id)
65}
66
67fn clear_timeout_interval(timeouts: &Arc<Mutex<Vec<Timeout>>>, id: usize) {
68 let mut timeouts = timeouts.lock().unwrap();
69 if let Some(timeout) = timeouts.iter_mut().find(|t| t.id == id) {
70 timeout.cb.take();
71 timeout.timeout = 0;
72 timeout.repeating = false;
73 }
74}
75
76pub struct TimersModule;
77
78impl ModuleDef for TimersModule {
79 fn declare(declare: &Declarations<'_>) -> Result<()> {
80 declare.declare("setTimeout")?;
81 declare.declare("clearTimeout")?;
82 declare.declare("setInterval")?;
83 declare.declare("clearInterval")?;
84 declare.declare("default")?;
85 Ok(())
86 }
87
88 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
89 let globals = ctx.globals();
90
91 export_default(ctx, exports, |default| {
92 let functions = ["setTimeout", "clearTimeout", "setInterval", "clearInterval"];
93 for func_name in functions {
94 let function: Function = globals.get(func_name)?;
95 default.set(func_name, function)?;
96 }
97 Ok(())
98 })?;
99
100 Ok(())
101 }
102}
103
104// impl From<TimersModule> for ModuleInfo<TimersModule> {
105// fn from(val: TimersModule) -> Self {
106// ModuleInfo {
107// name: "timers",
108// module: val,
109// }
110// }
111// }
112
113// pub fn init(ctx: &Ctx<'_>) -> Result<()> {
114// let globals = ctx.globals();
115
116// #[allow(clippy::arc_with_non_send_sync)]
117// let timeouts = Arc::new(Mutex::new(Vec::<Timeout>::new()));
118// let timeouts2 = timeouts.clone();
119// let timeouts3 = timeouts.clone();
120// let timeouts4 = timeouts.clone();
121
122// globals.set(
123// "setTimeout",
124// Func::from(move |ctx, cb, delay| set_timeout_interval(&ctx, &timeouts, cb, delay, false)),
125// )?;
126
127// globals.set(
128// "setInterval",
129// Func::from(move |ctx, cb, delay| set_timeout_interval(&ctx, &timeouts2, cb, delay, true)),
130// )?;
131
132// globals.set(
133// "clearTimeout",
134// Func::from(move |id: usize| clear_timeout_interval(&timeouts3, id)),
135// )?;
136
137// globals.set(
138// "clearInterval",
139// Func::from(move |id: usize| clear_timeout_interval(&timeouts4, id)),
140// )?;
141
142// globals.set("setImmediate", Func::from(set_immediate))?;
143
144// Ok(())
145// }
146
147// #[inline(always)]
148// fn poll_timers<'js>(ctx: &Ctx<'js>, timeouts: Arc<Mutex<Vec<Timeout<'js>>>>) -> Result<()> {
149// TIME_POLL_ACTIVE.store(true, Ordering::Relaxed);
150
151// ctx.spawn_exit(async move {
152// let mut interval = tokio::time::interval(Duration::from_millis(1));
153// let mut to_call = Some(Vec::new());
154// let mut exit_after_next_tick = false;
155// loop {
156// interval.tick().await;
157
158// let mut call_vec = to_call.take().unwrap(); //avoid creating a new vec
159// let current_time = get_current_time_millis();
160// let mut had_items = false;
161
162// timeouts.lock().unwrap().retain_mut(|timeout| {
163// had_items = true;
164// exit_after_next_tick = false;
165// if current_time > timeout.timeout {
166// if !timeout.repeating {
167// //do not clone if not not repeating
168// call_vec.push(timeout.cb.take());
169// return false;
170// }
171// timeout.timeout = current_time + timeout.delay;
172// call_vec.push(timeout.cb.clone());
173// }
174// true
175// });
176
177// for cb in call_vec.iter_mut() {
178// if let Some(cb) = cb.take() {
179// cb.call::<(), ()>(())?;
180// };
181// }
182
183// call_vec.clear();
184// to_call.replace(call_vec);
185
186// if !had_items {
187// if exit_after_next_tick {
188// break;
189// }
190// exit_after_next_tick = true;
191// }
192// }
193// TIME_POLL_ACTIVE.store(false, Ordering::Relaxed);
194
195// Ok(())
196// })?;
197// Ok(())
198// }