quickjs_runtime/quickjs_utils/
interrupthandler.rs

1use crate::quickjsruntimeadapter::QuickJsRuntimeAdapter;
2use libquickjs_sys as q;
3use std::ffi::c_void;
4use std::os::raw::c_int;
5
6//
7
8/// set an interrupt handler for the runtime
9/// # Safety
10/// be safe
11pub unsafe fn set_interrupt_handler(runtime: *mut q::JSRuntime, handler: q::JSInterruptHandler) {
12    q::JS_SetInterruptHandler(runtime, handler, std::ptr::null_mut());
13}
14
15pub(crate) fn init(q_js_rt: &QuickJsRuntimeAdapter) {
16    unsafe { set_interrupt_handler(q_js_rt.runtime, Some(interrupt_handler)) };
17}
18
19unsafe extern "C" fn interrupt_handler(_rt: *mut q::JSRuntime, _opaque: *mut c_void) -> c_int {
20    QuickJsRuntimeAdapter::do_with(|q_js_rt| {
21        let handler = q_js_rt.interrupt_handler.as_ref().unwrap();
22        i32::from(handler(q_js_rt))
23    })
24}
25
26#[cfg(test)]
27pub mod tests {
28    use crate::builder::QuickJsRuntimeBuilder;
29    use crate::jsutils::Script;
30    use crate::quickjs_utils::get_script_or_module_name_q;
31
32    use std::cell::RefCell;
33    use std::panic;
34    use std::sync::{Arc, Mutex};
35
36    #[test]
37    fn test_interrupt_handler() {
38        log::info!("interrupt_handler test");
39
40        let called = Arc::new(Mutex::new(RefCell::new(false)));
41        let called2 = called.clone();
42
43        /*panic::set_hook(Box::new(|panic_info| {
44            let backtrace = Backtrace::new();
45            println!("thread panic occurred: {panic_info}\nbacktrace: {backtrace:?}");
46            log::error!(
47                "thread panic occurred: {}\nbacktrace: {:?}",
48                panic_info,
49                backtrace
50            );
51        }));*/
52
53        //simple_logging::log_to_file("esruntime.log", LevelFilter::max())
54        //            .expect("could not init logger");
55
56        let rt = QuickJsRuntimeBuilder::new()
57            .set_interrupt_handler(move |qjs_rt| {
58                log::debug!("interrupt_handler called / 1");
59                let script_name = get_script_or_module_name_q(qjs_rt.get_main_realm());
60                match script_name {
61                    Ok(script_name) => {
62                        log::debug!("interrupt_handler called: {}", script_name);
63                    }
64                    Err(_) => {
65                        log::debug!("interrupt_handler called");
66                    }
67                }
68                let lck = called2.lock().unwrap();
69                *lck.borrow_mut() = true;
70                false
71            })
72            .build();
73
74        match rt.eval_sync(
75            None,
76            Script::new(
77                "test_interrupt.es",
78                "for (let x = 0; x < 10000; x++) {console.log('x' + x);}",
79            ),
80        ) {
81            Ok(_) => {}
82            Err(err) => {
83                panic!("err: {}", err);
84            }
85        }
86
87        rt.create_context("newctx").expect("ctx crea failed");
88        rt.exe_rt_task_in_event_loop(|q_js_rt| {
89            let ctx = q_js_rt.get_context("newctx");
90            match ctx.eval(Script::new(
91                "test_interrupt.es",
92                "for (let x = 0; x < 10000; x++) {console.log('x' + x);}",
93            )) {
94                Ok(_) => {}
95                Err(err) => {
96                    panic!("err: {}", err);
97                }
98            }
99        });
100
101        let lck = called.lock().unwrap();
102        assert!(*lck.borrow());
103    }
104}