
























@@ -41,6 +41,9 @@ import { linuxUpdateScript, macosUpdateScript, windowsUpdateScript } from "./npm
4141import { ensureVmRunning, resolveMacosVmName, resolveUbuntuVmName } from "./parallels-vm.ts";
4242import { runTimedUpdateJob } from "./update-job-timeout.ts";
434344+const LOGGED_PROCESS_TREE_EXIT_POLL_MS = 25;
45+const LOGGED_POST_FORCE_KILL_WAIT_MS = 1_000;
46+4447interface NpmUpdateOptions {
4548betaValidation?: string;
4649freshTargetSpec?: string;
@@ -151,6 +154,7 @@ export function spawnLoggedCommand(
151154let timedOut = false;
152155let settled = false;
153156let forceKillTimer: NodeJS.Timeout | undefined;
157+let forceKillAt: number | undefined;
154158const append = (text: string) => {
155159appendFileSync(logPath, text, "utf8");
156160onOutput(text);
@@ -164,6 +168,7 @@ export function spawnLoggedCommand(
164168`\n[${options.timeoutLabel ?? `${command} ${args.join(" ")}`} timed out after ${timeoutMs}ms]\n`,
165169);
166170signalLoggedChild(child, "SIGTERM");
171+forceKillAt = Date.now() + (options.timeoutKillGraceMs ?? freshLaneTimeoutKillGraceMs);
167172forceKillTimer = setTimeout(
168173() => signalLoggedChild(child, "SIGKILL"),
169174options.timeoutKillGraceMs ?? freshLaneTimeoutKillGraceMs,
@@ -193,11 +198,19 @@ export function spawnLoggedCommand(
193198settled = true;
194199clearTimeout(timeoutTimer);
195200clearTimeout(forceKillTimer);
196-if (timedOut && loggedProcessTreeIsAlive(child)) {
197-signalLoggedChild(child, "SIGKILL");
201+const finish = () => {
202+forceKillAt = undefined;
203+untrackLoggedChild(child);
204+resolve(timedOut ? 124 : (code ?? 1));
205+};
206+if (timedOut) {
207+void finishTimedOutLoggedProcessTree(child, {
208+ forceKillAt,
209+timeoutKillGraceMs: options.timeoutKillGraceMs ?? freshLaneTimeoutKillGraceMs,
210+}).then(finish, finish);
211+return;
198212}
199-untrackLoggedChild(child);
200-resolve(timedOut ? 124 : (code ?? 1));
213+finish();
201214});
202215});
203216}
@@ -266,6 +279,43 @@ function loggedProcessTreeIsAlive(child: ReturnType<typeof spawn>): boolean {
266279}
267280}
268281282+async function waitForLoggedProcessTreeExit(
283+child: ReturnType<typeof spawn>,
284+timeoutMs: number,
285+): Promise<boolean> {
286+const deadlineAt = Date.now() + timeoutMs;
287+while (Date.now() < deadlineAt) {
288+if (!loggedProcessTreeIsAlive(child)) {
289+return true;
290+}
291+await new Promise((resolvePoll) => {
292+setTimeout(resolvePoll, LOGGED_PROCESS_TREE_EXIT_POLL_MS);
293+});
294+}
295+return !loggedProcessTreeIsAlive(child);
296+}
297+298+async function finishTimedOutLoggedProcessTree(
299+child: ReturnType<typeof spawn>,
300+{
301+ forceKillAt,
302+ timeoutKillGraceMs,
303+}: { forceKillAt: number | undefined; timeoutKillGraceMs: number },
304+): Promise<void> {
305+if (!loggedProcessTreeIsAlive(child)) {
306+return;
307+}
308+const graceRemainingMs =
309+forceKillAt === undefined ? timeoutKillGraceMs : Math.max(0, forceKillAt - Date.now());
310+if (graceRemainingMs > 0) {
311+await waitForLoggedProcessTreeExit(child, graceRemainingMs);
312+}
313+if (loggedProcessTreeIsAlive(child)) {
314+signalLoggedChild(child, "SIGKILL");
315+}
316+await waitForLoggedProcessTreeExit(child, LOGGED_POST_FORCE_KILL_WAIT_MS);
317+}
318+269319function signalLoggedChild(child: ReturnType<typeof spawn>, signal: NodeJS.Signals) {
270320if (process.platform !== "win32" && typeof child.pid === "number") {
271321try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。