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// }