Skip to content

Commit

Permalink
fix(dev): wait until all app server descendants have been killed (#6663)
Browse files Browse the repository at this point in the history
  • Loading branch information
pcattori committed Jun 21, 2023
1 parent 38cb79e commit d86a41a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-dots-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

fix `remix dev -c`: kill all descendant processes of specified command when restarting
29 changes: 5 additions & 24 deletions packages/remix-dev/devServer_unstable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type { Result } from "../result";
import { err, ok } from "../result";
import invariant from "../invariant";
import { logger } from "../tux";
import { kill, killtree } from "./proc";

type Origin = {
scheme: string;
Expand Down Expand Up @@ -214,7 +215,9 @@ export let serve = async (
try {
let start = Date.now();
if (state.appServer === undefined || options.restart) {
await kill(state.appServer);
if (state.appServer?.pid) {
await killtree(state.appServer.pid);
}
state.appServer = startAppServer(options.command);
}
let appReady = await state.appReady!.result;
Expand Down Expand Up @@ -276,7 +279,7 @@ export let serve = async (
server.listen(origin.port);

return new Promise(() => {}).finally(async () => {
await kill(state.appServer);
state.appServer?.pid && (await kill(state.appServer.pid));
websocket.close();
server.close();
await dispose();
Expand All @@ -290,25 +293,3 @@ let clean = (config: RemixConfig) => {
};

let relativePath = (file: string) => path.relative(process.cwd(), file);

let kill = async (p?: execa.ExecaChildProcess) => {
if (p === undefined) return;
let channel = Channel.create<void>();
p.on("exit", channel.ok);

// https://github.com/nodejs/node/issues/12378
if (process.platform === "win32") {
try {
await execa("taskkill", ["/pid", String(p.pid), "/f", "/t"]);
} catch (error) {
// if exit code is 128, app server process is already dead
if (!(error instanceof Error)) throw error;
if (!("exitCode" in error)) throw error;
if (error.exitCode !== 128) throw error;
}
} else {
p.kill("SIGTERM", { forceKillAfterTimeout: 1_000 });
}

await channel.result;
};
48 changes: 48 additions & 0 deletions packages/remix-dev/devServer_unstable/proc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import execa from "execa";
import pidtree from "pidtree";

let isWindows = process.platform === "win32";

export let kill = async (pid: number) => {
try {
let cmd = isWindows
? ["taskkill", "/F", "/PID", pid.toString()]
: ["kill", "-9", pid.toString()];
await execa(cmd[0], cmd.slice(1));
} catch (error) {
throw new Error(`Failed to kill process ${pid}: ${error}`);
}
};

let isAlive = (pid: number) => {
try {
process.kill(pid, 0);
return true;
} catch (error) {
return false;
}
};

export let killtree = async (pid: number) => {
let descendants = await pidtree(pid);
let pids = [pid, ...descendants];

await Promise.all(pids.map(kill));

return new Promise<void>((resolve, reject) => {
let check = setInterval(() => {
let alive = pids.filter(isAlive);
if (alive.length === 0) {
clearInterval(check);
resolve();
}
}, 50);

setTimeout(() => {
clearInterval(check);
reject(
new Error("Timeout: Processes did not exit within the specified time.")
);
}, 2000);
});
};
1 change: 1 addition & 0 deletions packages/remix-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"ora": "^5.4.1",
"picocolors": "^1.0.0",
"picomatch": "^2.3.1",
"pidtree": "^0.6.0",
"postcss": "^8.4.19",
"postcss-discard-duplicates": "^5.1.0",
"postcss-load-config": "^4.0.1",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10947,6 +10947,11 @@ pidtree@^0.3.0:
resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz"
integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==

pidtree@^0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==

pify@^2.2.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
Expand Down

0 comments on commit d86a41a

Please sign in to comment.