

























@@ -140,70 +140,89 @@ describe("managed-child-process", () => {
140140}
141141});
142142143-posixIt("kills the managed child process group when the runner is terminated", async () => {
144-const dir = createTempDir("openclaw-managed-child-");
145-const childPath = path.join(dir, "child.mjs");
146-const runnerPath = path.join(dir, "runner.mjs");
147-const childPidPath = path.join(dir, "child.pid");
148-const runnerReadyPath = path.join(dir, "runner.ready");
149-const helperUrl = pathToFileURL(path.resolve("scripts/lib/managed-child-process.mjs")).href;
150-151-fs.writeFileSync(
152-childPath,
153-`
154-import fs from "node:fs";
155-156-fs.writeFileSync(process.argv[2], String(process.pid));
157-for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
158- process.on(signal, () => process.exit(0));
159-}
143+posixIt(
144+"kills managed child process group descendants when the runner is terminated",
145+async () => {
146+const dir = createTempDir("openclaw-managed-child-");
147+const childPath = path.join(dir, "child.mjs");
148+const runnerPath = path.join(dir, "runner.mjs");
149+const childPidPath = path.join(dir, "child.pid");
150+const descendantPidPath = path.join(dir, "descendant.pid");
151+const runnerReadyPath = path.join(dir, "runner.ready");
152+const helperUrl = pathToFileURL(path.resolve("scripts/lib/managed-child-process.mjs")).href;
153+154+fs.writeFileSync(
155+childPath,
156+`
157+ import { spawn } from "node:child_process";
158+ import fs from "node:fs";
159+160+ const descendant = spawn(process.execPath, [
161+ "-e",
162+ "process.on('SIGTERM', () => {}); setInterval(() => {}, 1000);",
163+ ], { stdio: "ignore" });
164+ fs.writeFileSync(process.argv[2], String(process.pid));
165+ fs.writeFileSync(process.argv[3], String(descendant.pid));
166+ for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
167+ process.on(signal, () => process.exit(0));
168+ }
160169setInterval(() => {}, 1_000);
161170`,
162-"utf8",
163-);
164-fs.writeFileSync(
165-runnerPath,
166-`
171+ "utf8",
172+ );
173+ fs.writeFileSync(
174+ runnerPath,
175+ `
167176import fs from "node:fs";
168177import { runManagedCommand } from ${JSON.stringify(helperUrl)};
169178170-process.exitCode = await runManagedCommand({
171- bin: process.execPath,
172- args: [${JSON.stringify(childPath)}, ${JSON.stringify(childPidPath)}],
173- stdio: "ignore",
174- onReady: () => fs.writeFileSync(${JSON.stringify(runnerReadyPath)}, "1"),
175-});
179+ process.exitCode = await runManagedCommand({
180+ bin: process.execPath,
181+ args: [${JSON.stringify(childPath)}, ${JSON.stringify(childPidPath)}, ${JSON.stringify(descendantPidPath)}],
182+ stdio: "ignore",
183+ onReady: () => fs.writeFileSync(${JSON.stringify(runnerReadyPath)}, "1"),
184+ });
176185`,
177-"utf8",
178-);
179-180-const runner = spawn(process.execPath, [runnerPath], {
181-stdio: "ignore",
182-});
183-const runnerPid = expectProcessPid(runner.pid);
184-let childPid = 0;
186+"utf8",
187+);
185188186-try {
187-await waitFor(() => fs.existsSync(runnerReadyPath));
188-await waitFor(() => fs.existsSync(childPidPath));
189-childPid = Number(fs.readFileSync(childPidPath, "utf8"));
190-expect(Number.isInteger(childPid)).toBe(true);
191-expect(isProcessAlive(childPid)).toBe(true);
192-193-process.kill(runnerPid, "SIGTERM");
194-const result = await waitForClose(runner);
195-196-expect(result).toEqual({ code: 143, signal: null });
197-await waitFor(() => !isProcessAlive(childPid), 1_500);
198-} finally {
199-if (isProcessAlive(runnerPid)) {
200-process.kill(runnerPid, "SIGKILL");
201-}
202-if (childPid && isProcessAlive(childPid)) {
203-process.kill(childPid, "SIGKILL");
189+const runner = spawn(process.execPath, [runnerPath], {
190+stdio: "ignore",
191+});
192+const runnerPid = expectProcessPid(runner.pid);
193+let childPid = 0;
194+let descendantPid = 0;
195+196+try {
197+await waitFor(() => fs.existsSync(runnerReadyPath));
198+await waitFor(() => fs.existsSync(childPidPath));
199+await waitFor(() => fs.existsSync(descendantPidPath));
200+childPid = Number(fs.readFileSync(childPidPath, "utf8"));
201+descendantPid = Number(fs.readFileSync(descendantPidPath, "utf8"));
202+expect(Number.isInteger(childPid)).toBe(true);
203+expect(Number.isInteger(descendantPid)).toBe(true);
204+expect(isProcessAlive(childPid)).toBe(true);
205+expect(isProcessAlive(descendantPid)).toBe(true);
206+207+process.kill(runnerPid, "SIGTERM");
208+const result = await waitForClose(runner);
209+210+expect(result).toEqual({ code: 143, signal: null });
211+await waitFor(() => !isProcessAlive(childPid), 1_500);
212+await waitFor(() => !isProcessAlive(descendantPid), 1_500);
213+} finally {
214+if (isProcessAlive(runnerPid)) {
215+process.kill(runnerPid, "SIGKILL");
216+}
217+if (childPid && isProcessAlive(childPid)) {
218+process.kill(childPid, "SIGKILL");
219+}
220+if (descendantPid && isProcessAlive(descendantPid)) {
221+process.kill(descendantPid, "SIGKILL");
222+}
204223}
205-}
206-});
224+},
225+);
207226});
208227209228async function waitFor(condition: () => boolean, timeoutMs = 3_000) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。