diff --git a/src/jsc/bindings/c-bindings.cpp b/src/jsc/bindings/c-bindings.cpp index a20749bef690..ddcfc7e8c406 100644 --- a/src/jsc/bindings/c-bindings.cpp +++ b/src/jsc/bindings/c-bindings.cpp @@ -933,9 +933,11 @@ static struct sigaction previous_actions[NSIG]; M(SIGIO); #if OS(LINUX) +// SIGPWR is intentionally omitted: JavaScriptCore uses it to suspend and +// resume threads for garbage collection (see ThreadingPOSIX.cpp). Replacing +// that handler here would break GC and terminate the process with SIGPWR. #define FOR_EACH_LINUX_ONLY_SIGNAL(M) \ M(SIGPOLL); \ - M(SIGPWR); \ M(SIGSTKFLT); #endif diff --git a/test/js/bun/util/open-in-editor-gc.test.ts b/test/js/bun/util/open-in-editor-gc.test.ts new file mode 100644 index 000000000000..fb82f3c15928 --- /dev/null +++ b/test/js/bun/util/open-in-editor-gc.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, isLinux } from "harness"; + +// On Linux, JavaScriptCore uses SIGPWR to suspend and resume threads during +// garbage collection. Bun.openInEditor() spawns the editor via bun.spawnSync +// on a detached background thread, which installs signal-forwarding handlers +// for the duration of the spawn. Those handlers must not replace JSC's SIGPWR +// handler, otherwise a concurrent GC will terminate the process with SIGPWR. +test.skipIf(!isLinux)("Bun.openInEditor concurrent with GC does not terminate the process with SIGPWR", () => { + const script = ` + for (let k = 0; k < 50; k++) { + try { Bun.openInEditor("foo" + k); } catch {} + } + for (let i = 0; i < 200; i++) Bun.gc(true); + `; + const { exitCode, signalCode } = Bun.spawnSync({ + cmd: [bunExe(), "-e", script], + env: { ...bunEnv, EDITOR: undefined, VISUAL: undefined }, + stdout: "ignore", + stderr: "ignore", + }); + expect(signalCode).toBeUndefined(); + expect(exitCode).toBe(0); +});