stopWhenThisEnds

tcmts.cli.test.e2e.harness.stopWhenThisEnds · #3jsbq2zd · term · runs on the server

uses node:child_process

Stop a process this one started once this one ends, however it ends: killed (SIGKILL, a hung test's time limit), it can't stop anything itself, and what it started would run on for good, orphaned. A watchdog process (one for this process, started with the first it's told of: WATCHDOG) is told each process kept and each that ends; this process's end closes the pipe to it, and it stops those still running: asked to (SIGTERM), then made to (SIGKILL) if one hasn't in 5 seconds. (Something kept that isn't a child process, a server in this one, is withChildren's.)

import { WATCHDOG, watchdogs } from "#codebase/tcmts/cli/test/e2e/harness";
import { spawn } from "node:child_process";
import type { ChildProcess } from "node:child_process";

export function stopWhenThisEnds(c: ChildProcess): void {
  if (typeof c.once !== "function" || !c.pid) return;
  let w = watchdogs.get(process.pid);
  if (!w) {
    // (its own process group, so it outlives a stop of this one's; nothing of this one's environment: it isn't a
    // tcm, and runs no code of the codebase)
    w = spawn(process.execPath, ["-e", WATCHDOG], {
      stdio: ["pipe", "ignore", "ignore"],
      detached: true,
      env: { PATH: process.env.PATH ?? "" },
    });
    // (it doesn't keep this process alive, nor fail it if it goes)
    w.unref();
    w.stdin!.on("error", () => {});
    (w.stdin as unknown as { unref(): void }).unref();
    watchdogs.set(process.pid, w);
  }
  const pipe = w.stdin!;
  pipe.write(`+${c.pid}\n`);
  // (so a pid used again later, by something else, isn't stopped)
  c.once("exit", () => pipe.write(`-${c.pid}\n`));
}

uses

used by