Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
7471dc6bfc fix(worker): stop JS execution immediately on process.exit()
When process.exit() was called in a worker thread, JavaScript execution
continued after the call instead of stopping immediately. This was
because the worker's exit() function only woke the event loop without
firing the JSC termination trap.

The fix adds a call to vm.jsc_vm.notifyNeedTermination() in the exit()
function (called only when process.exit() is used on the worker thread).
This fires the NeedTermination trap in JavaScriptCore, immediately
halting JavaScript execution - matching Node.js behavior.

Note: The termination trap is only called in exit(), not in
notifyNeedTermination(), because the latter can be called from any
thread (e.g., when worker.terminate() is called from the main thread).

Fixes #26239

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 08:00:31 +00:00
2 changed files with 42 additions and 1 deletions

View File

@@ -559,6 +559,12 @@ pub fn setRefInternal(this: *WebWorker, value: bool) void {
/// Implement process.exit(). May only be called from the Worker thread.
pub fn exit(this: *WebWorker) void {
this.exit_called = true;
// Fire termination trap to immediately halt JS execution.
// This must be done before notifyNeedTermination() so that any
// remaining JS code doesn't continue executing.
if (this.vm) |vm| {
vm.jsc_vm.notifyNeedTermination();
}
this.notifyNeedTermination();
}
@@ -574,7 +580,6 @@ pub fn notifyNeedTermination(this: *WebWorker) callconv(.c) void {
if (this.vm) |vm| {
vm.eventLoop().wakeup();
// TODO(@190n) notifyNeedTermination
}
// TODO(@190n) delete

View File

@@ -0,0 +1,36 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
// Regression test for https://github.com/oven-sh/bun/issues/26239
// process.exit() in a worker should immediately stop JavaScript execution
test("process.exit() in worker stops execution immediately", async () => {
using dir = tempDir("issue-26239", {
"worker_exit.js": `
import { Worker, isMainThread } from "node:worker_threads";
if (isMainThread) {
const worker = new Worker(new URL(import.meta.url));
worker.on("exit", (code) => process.exit(code ?? 0));
} else {
console.log("before exit");
process.exit(0);
console.log("after exit"); // This should NOT be printed
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "worker_exit.js"],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// "after exit" should NOT appear in the output - the bug was that JS continued executing
expect(stdout).toContain("before exit");
expect(stdout).not.toContain("after exit");
expect(exitCode).toBe(0);
});