Merge pull request #2648 from lightpanda-io/worker_isolate_terminate

Make worker more robust in the face of a disconnected CDP client
This commit is contained in:
Karl Seguin
2026-06-05 18:27:01 +08:00
committed by GitHub
2 changed files with 24 additions and 2 deletions

View File

@@ -514,6 +514,14 @@ pub fn isExecutionTerminating(self: *const Env) bool {
return v8.v8__Isolate__IsExecutionTerminating(self.isolate.handle);
}
// Whether a forcible terminate has been requested (and not yet cleared by
// cancelTerminate). Unlike isExecutionTerminating, this is our own sticky
// flag, so it stays true after V8 consumes the terminate on the JSEntry
// unwind. Callers about to enter a fresh eval use it to refuse to run.
pub fn terminatePending(self: *const Env) bool {
return self.terminate_requested.load(.acquire);
}
pub fn terminate(self: *Env) void {
self.terminate_mutex.lock();
defer self.terminate_mutex.unlock();

View File

@@ -182,6 +182,12 @@ fn httpDoneCallback(ctx: *anyopaque) !void {
}
fn loadInitialScript(self: *Worker, script: []const u8) !void {
const js_context = self._worker_scope.js;
if (js_context.env.terminatePending()) {
return;
}
// Keep buffering throughout the entire outer eval (including any
// runMacrotasks pumped by importScripts via the synchronous CDP path,
// see WorkerGlobalScope.importScripts). The flip-and-drain happens
@@ -200,7 +206,7 @@ fn loadInitialScript(self: *Worker, script: []const u8) !void {
}
var ls: js.Local.Scope = undefined;
self._worker_scope.js.localScope(&ls);
js_context.localScope(&ls);
defer ls.deinit();
var try_catch: js.TryCatch = undefined;
@@ -213,12 +219,20 @@ fn loadInitialScript(self: *Worker, script: []const u8) !void {
// synchronously through ScriptManagerBase (client.tick sync_wait).
switch (self._type) {
.classic => _ = ls.local.eval(script, self._url) catch |err| {
if (js_context.env.terminatePending()) {
return;
}
const caught = try_catch.caughtOrError(self._arena, err);
log.err(.browser, "worker script error", .{ .url = self._url, .caught = caught });
self.fireErrorEvent(caught.exception orelse @errorName(err), null);
return;
},
.module => self._worker_scope.js.module(false, &ls.local, script, self._url, true) catch |err| {
.module => js_context.module(false, &ls.local, script, self._url, true) catch |err| {
if (js_context.env.terminatePending()) {
return;
}
const caught = try_catch.caughtOrError(self._arena, err);
log.err(.browser, "worker module error", .{ .url = self._url, .caught = caught });
self.fireErrorEvent(caught.exception orelse @errorName(err), null);