mirror of
https://github.com/morluto/rea.git
synced 2026-11-08 07:46:28 +00:00
fix(process): bound output draining after a stoppable command's grace
execFileOutput settles only when every output pipe closes. When a stoppable command printed, exited, and left a descendant holding its stdout, the deadline and SIGKILL grace had no process left to stop, so callers such as the Kuna doctor version probe waited for the descendant instead of settling within the deadline plus grace. Destroy the command's output streams when the grace expires so the timeout is reported on time. Fixes #5557 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c2858d2aa0
commit
b7c2646dcf
2 files changed
+43
-1
No files matched your search
@@ -272,6 +272,41 @@ describe.skipIf(process.platform === "win32")(
|
||||
}
|
||||
});
|
||||
|
||||
it("settles after the grace when an exited command's descendant holds its output open", async () => {
|
||||
const directory = await createTestTempDirectory("rea-exec-descendant-");
|
||||
const pidFile = join(directory, "descendant.pid");
|
||||
// The command prints, then exits at once, leaving a descendant that
|
||||
// inherited stdout and stderr and keeps both pipes open for 60 s.
|
||||
const script = [
|
||||
'const { spawn } = require("node:child_process");',
|
||||
'const fs = require("node:fs");',
|
||||
'const child = spawn(process.execPath, ["-e", "setTimeout(() => {}, 60000)"], { stdio: ["ignore", "inherit", "inherit"] });',
|
||||
`fs.writeFileSync(${JSON.stringify(pidFile)}, String(child.pid));`,
|
||||
"child.unref();",
|
||||
'process.stdout.write("kuna 1.833\\n");',
|
||||
].join("\n");
|
||||
const started = Date.now();
|
||||
try {
|
||||
await expect(
|
||||
execFileOutput(process.execPath, ["-e", script], {
|
||||
timeout: 500,
|
||||
stopSignal: "SIGTERM",
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "ETIMEDOUT", killed: true });
|
||||
// Deadline plus the 5 s stop grace, with scheduling slack.
|
||||
expect(Date.now() - started).toBeLessThan(15_000);
|
||||
} finally {
|
||||
const pid = Number(await readFile(pidFile, "utf8").catch(() => ""));
|
||||
if (Number.isSafeInteger(pid) && pid > 0)
|
||||
try {
|
||||
process.kill(pid, "SIGKILL");
|
||||
} catch {
|
||||
// The descendant may already have exited.
|
||||
}
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
}, 30_000);
|
||||
|
||||
it("kills a timed-out process that ignores the stop signal", async () => {
|
||||
const script = [
|
||||
'for (const name of ["SIGINT", "SIGTERM"]) process.on(name, () => {});',
|
||||
|
||||
Reference in new issue
Block a user