From d988217e54845a770798ae9bfb783e419f1a6d5e Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Wed, 21 Oct 2026 12:44:12 +0100 Subject: [PATCH 1/4] feat: Allow resetting the engine at runtime --- include/extension-api.h | 1 + runtime/engine.cpp | 9 +++++++++ runtime/event_loop.cpp | 9 +++++++++ runtime/event_loop.h | 6 ++++++ 4 files changed, 25 insertions(+) diff --git a/include/extension-api.h b/include/extension-api.h index 55af98e7..68da8ab6 100644 --- a/include/extension-api.h +++ b/include/extension-api.h @@ -172,6 +172,7 @@ class Engine { void report_unhandled_promise_rejections(); static void clear_unhandled_promise_rejections(); + void reset(); void abort(const char *reason); static bool debug_logging_enabled(); diff --git a/runtime/engine.cpp b/runtime/engine.cpp index e9da55dd..3908e56a 100644 --- a/runtime/engine.cpp +++ b/runtime/engine.cpp @@ -3,6 +3,7 @@ #include "debugger.h" #include "encode.h" #include "event_loop.h" +#include "../builtins/web/timers.h" #include "script_loader.h" #include "js/CompilationAndEvaluation.h" @@ -708,3 +709,11 @@ void Engine::report_unhandled_promise_rejections() { void Engine::clear_unhandled_promise_rejections() { JS::SetClear(CONTEXT, unhandledRejectedPromises); } +void Engine::reset() { + clear_unhandled_promise_rejections(); + core::EventLoop::reset(this); + builtins::web::timers::reset(); + core::EventLoop::reset(this); + JS::PrepareForFullGC(cx()); + JS::NonIncrementalGC(cx(), JS::GCOptions::Normal, JS::GCReason::API); +} \ No newline at end of file diff --git a/runtime/event_loop.cpp b/runtime/event_loop.cpp index cdcdaabf..9cfea77f 100644 --- a/runtime/event_loop.cpp +++ b/runtime/event_loop.cpp @@ -99,4 +99,13 @@ bool EventLoop::run_event_loop(api::Engine *engine, double total_compute) { void EventLoop::init(JSContext *cx) { queue.init(cx); } +void EventLoop::reset(api::Engine *engine) { + auto &q = queue.get(); + for (auto task : q.tasks) { + task->cancel(engine); + } + q.tasks.clear(); + q.interest_cnt = 0; +} + } // namespace core diff --git a/runtime/event_loop.h b/runtime/event_loop.h index acca516c..27838bfd 100644 --- a/runtime/event_loop.h +++ b/runtime/event_loop.h @@ -41,6 +41,12 @@ class EventLoop { * Remove a queued async task. */ static bool cancel_async_task(api::Engine *engine, const RefPtr& task); + + /** + * Reset the event loop state, cancelling all pending tasks and clearing interest count. + * Used between requests in reusable sandboxes. + */ + static void reset(api::Engine *engine); }; } // namespace core From fc9e28e4291dbdc6e295188e08cb07518b22b256 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Wed, 21 Oct 2026 12:46:29 +0100 Subject: [PATCH 2/4] cleanup --- runtime/engine.cpp | 2 +- runtime/event_loop.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/engine.cpp b/runtime/engine.cpp index 3908e56a..ae5f025a 100644 --- a/runtime/engine.cpp +++ b/runtime/engine.cpp @@ -716,4 +716,4 @@ void Engine::reset() { core::EventLoop::reset(this); JS::PrepareForFullGC(cx()); JS::NonIncrementalGC(cx(), JS::GCOptions::Normal, JS::GCReason::API); -} \ No newline at end of file +} diff --git a/runtime/event_loop.h b/runtime/event_loop.h index 27838bfd..d8354e1b 100644 --- a/runtime/event_loop.h +++ b/runtime/event_loop.h @@ -44,7 +44,6 @@ class EventLoop { /** * Reset the event loop state, cancelling all pending tasks and clearing interest count. - * Used between requests in reusable sandboxes. */ static void reset(api::Engine *engine); }; From 93f2026934d054ed145e1f417c94a4b652f1f057 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Wed, 21 Oct 2026 14:59:34 +0100 Subject: [PATCH 3/4] Add timers changes --- builtins/web/timers.cpp | 8 ++++++++ builtins/web/timers.h | 1 + 2 files changed, 9 insertions(+) diff --git a/builtins/web/timers.cpp b/builtins/web/timers.cpp index d4e9a8cb..fe3519b7 100644 --- a/builtins/web/timers.cpp +++ b/builtins/web/timers.cpp @@ -229,4 +229,12 @@ bool install(api::Engine *engine) { return JS_DefineFunctions(engine->cx(), engine->global(), methods); } +void reset() { + for (auto &[id, timer] : TIMERS_MAP->timers_) { + timer->cancel(ENGINE); + } + TIMERS_MAP->timers_.clear(); + TIMERS_MAP->next_timer_id = 1; +} + } // namespace builtins::web::timers diff --git a/builtins/web/timers.h b/builtins/web/timers.h index f88d36bd..38a6cba7 100644 --- a/builtins/web/timers.h +++ b/builtins/web/timers.h @@ -14,6 +14,7 @@ bool set_interval(JSContext *cx, HandleObject handler, JS::HandleValueVector arg void clear_timeout_or_interval(int32_t timer_id); bool install(api::Engine *engine); +void reset(); } // namespace builtins::web::timers From 7af06c78e503cd997a588247e8072c81ad3efa92 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Mon, 26 Oct 2026 12:42:49 +0000 Subject: [PATCH 4/4] Fix linter error --- runtime/event_loop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/event_loop.cpp b/runtime/event_loop.cpp index 9cfea77f..a41994b8 100644 --- a/runtime/event_loop.cpp +++ b/runtime/event_loop.cpp @@ -101,7 +101,7 @@ void EventLoop::init(JSContext *cx) { queue.init(cx); } void EventLoop::reset(api::Engine *engine) { auto &q = queue.get(); - for (auto task : q.tasks) { + for (const auto& task : q.tasks) { task->cancel(engine); } q.tasks.clear();