From 8b8bee4e9c56036b50b42236ef776d1275967e19 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 21 Jan 2026 15:40:32 +0800 Subject: [PATCH] Move runMicrotask from Context to Local This ensures that there's always a HandleScope avaialble when running microtasks --- src/browser/EventManager.zig | 10 ++++++++-- src/browser/Page.zig | 21 ++++++++------------- src/browser/ScriptManager.zig | 2 +- src/browser/js/Context.zig | 10 +++------- src/browser/js/Local.zig | 4 ++++ src/browser/js/PromiseResolver.zig | 4 ++-- src/browser/webapi/Window.zig | 2 +- 7 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index 93227ad5..fb6d3674 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -137,7 +137,10 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void var was_handled = false; defer if (was_handled) { - self.page.js.runMicrotasks(); + var ls: js.Local.Scope = undefined; + self.page.js.localScope(&ls); + defer ls.deinit(); + ls.local.runMicrotasks(); }; switch (target._type) { @@ -180,7 +183,10 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E var was_dispatched = false; defer if (was_dispatched) { - self.page.js.runMicrotasks(); + var ls: js.Local.Scope = undefined; + self.page.js.localScope(&ls); + defer ls.deinit(); + ls.local.runMicrotasks(); }; if (function_) |func| { diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 802cf66a..b5bc29af 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -205,9 +205,14 @@ pub fn deinit(self: *Page) void { // stats.print(&stream) catch unreachable; } - // some MicroTasks might be referencing the page, we need to drain it while - // the page still exists - self.js.runMicrotasks(); + { + // some MicroTasks might be referencing the page, we need to drain it while + // the page still exists + var ls: JS.Local.Scope = undefined; + self.js.localScope(&ls); + defer ls.deinit(); + ls.local.runMicrotasks(); + } const session = self._session; session.executor.removeContext(); @@ -958,16 +963,6 @@ fn printWaitAnalysis(self: *Page) void { } } -pub fn tick(self: *Page) void { - if (comptime IS_DEBUG) { - log.debug(.page, "tick", .{}); - } - _ = self.scheduler.run() catch |err| { - log.err(.page, "tick", .{ .err = err }); - }; - self.js.runMicrotasks(); -} - pub fn isGoingAway(self: *const Page) bool { return self._queued_navigation != null; } diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index ff6d1ae0..aa0b7dc6 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -843,7 +843,7 @@ pub const Script = struct { defer { // We should run microtasks even if script execution fails. - page.js.runMicrotasks(); + local.runMicrotasks(); _ = page.scheduler.run() catch |err| { log.err(.page, "scheduler", .{ .err = err }); }; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index e0d1f3de..8f1db68a 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -384,10 +384,6 @@ fn newFunctionWithData(local: *const js.Local, comptime callback: *const fn (?*c }; } -pub fn runMicrotasks(self: *Context) void { - self.isolate.performMicrotasksCheckpoint(); -} - // == Callbacks == // Callback from V8, asking us to load a module. The "specifier" is // the src of the module to load. @@ -669,7 +665,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM } fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, module_entry: ModuleEntry, local: *const js.Local) void { - defer self.runMicrotasks(); + defer local.runMicrotasks(); // we can only be here if the module has been evaluated and if // we have a resolve loading this asynchronously. @@ -706,7 +702,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul return; } const l = c.local; - defer l.ctx.runMicrotasks(); + defer l.runMicrotasks(); const namespace = l.toLocal(s.module.?).getModuleNamespace(); _ = l.toLocal(s.resolver).resolve("resolve namespace", namespace); } @@ -728,7 +724,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul return; } - defer ctx.runMicrotasks(); + defer l.runMicrotasks(); _ = l.toLocal(s.resolver).reject("catch callback", js.Value{ .local = l, .handle = v8.v8__FunctionCallbackInfo__Data(callback_handle).?, diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 55e92280..80b885f0 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -72,6 +72,10 @@ pub fn newArray(self: *const Local, len: u32) js.Array { }; } +pub fn runMicrotasks(self: *const Local) void { + self.isolate.performMicrotasksCheckpoint(); +} + // == Executors == pub fn eval(self: *const Local, src: []const u8, name: ?[]const u8) !void { _ = try self.exec(src, name); diff --git a/src/browser/js/PromiseResolver.zig b/src/browser/js/PromiseResolver.zig index 9e527a88..183effee 100644 --- a/src/browser/js/PromiseResolver.zig +++ b/src/browser/js/PromiseResolver.zig @@ -54,7 +54,7 @@ fn _resolve(self: PromiseResolver, value: anytype) !void { if (!out.has_value or !out.value) { return error.FailedToResolvePromise; } - local.ctx.runMicrotasks(); + local.runMicrotasks(); } pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype) void { @@ -72,7 +72,7 @@ fn _reject(self: PromiseResolver, value: anytype) !void { if (!out.has_value or !out.value) { return error.FailedToRejectPromise; } - local.ctx.runMicrotasks(); + local.runMicrotasks(); } pub fn persist(self: PromiseResolver) !Global { diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 781081c4..43391bfb 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -586,7 +586,7 @@ const ScheduleCallback = struct { defer self.deinit(); _ = page.window._timers.remove(self.timer_id); - page.js.runMicrotasks(); + ls.local.runMicrotasks(); return null; } };