Skip to content

Commit

Permalink
Merge pull request #82 from gadget-inc/process-groups
Browse files Browse the repository at this point in the history
Process groups
  • Loading branch information
airhorns committed Jun 9, 2023
2 parents fc0bf04 + 84e449a commit d9833a5
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 50 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"@types/find-root": "^1.1.2",
"@types/jest": "^27.4.0",
"@types/lodash": "^4.14.194",
"@types/node": "^14.14.14",
"@types/node": "^18.11.9",
"@types/yargs": "^15.0.14",
"eslint": "^8.40.0",
"eslint-plugin-jest": "^27.2.1",
Expand Down
50 changes: 23 additions & 27 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion spec/Supervisor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ChildProcess, spawn } from "child_process";
import { range } from "lodash";
import * as path from "path";

jest.setTimeout(10000);

const childExit = (child: ChildProcess) => {
return new Promise<void>((resolve) => {
child.on("exit", (code: number) => {
Expand Down Expand Up @@ -106,4 +108,4 @@ test("it doesn't have any stdin if wds is started with terminal commands", async
await childExit(child);

expect(output).toEqual("");
});
});
4 changes: 2 additions & 2 deletions src/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export class Project {
this.supervisor.restart();
}

shutdown(code = 0) {
this.supervisor.stop();
async shutdown(code = 0) {
await this.supervisor.stop();
for (const cleanup of this.cleanups) {
cleanup();
}
Expand Down
47 changes: 31 additions & 16 deletions src/Supervisor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { setTimeout } from "timers/promises";
import type { ChildProcess, StdioOptions } from "child_process";
import { spawn } from "child_process";
import { EventEmitter } from "events";
import { EventEmitter, once } from "events";
import type { RunOptions } from "./Options";
import type { Project } from "./Project";
import { log } from "./utils";
Expand All @@ -12,28 +13,41 @@ export class Supervisor extends EventEmitter {
super();
}

stop() {
if (this.process) {
this.process.kill("SIGTERM");
/**
* Stop the process with a graceful SIGTERM, then SIGKILL after a timeout
* Kills the whole process group so that any subprocesses of the process are also killed
* See https://azimi.me/2014/12/31/kill-child_process-node-js.html for more information
*/
async stop() {
if (!this.process || !this.process.pid) return;

const ref = this.process;
const exit = once(ref, "exit");
this.kill("SIGTERM");

await Promise.race([exit, setTimeout(5000)]);
if (!ref.killed) {
this.kill("SIGKILL", ref.pid);
}
const process = this.process;
setTimeout(() => {
if (!process.killed) {
process.kill("SIGKILL");
}
}, 5000);
}

kill() {
if (this.process) {
this.process.kill("SIGKILL");
kill(signal = "SIGKILL", pid = this.process?.pid) {
if (pid) {
try {
process.kill(-pid, signal);
} catch (error: any) {
if (error.code == "ESRCH") {
// process can't be found, is already dead
return;
} else {
throw error;
}
}
}
}

restart() {
if (this.process) {
this.process.kill("SIGKILL");
}
this.kill();

const stdio: StdioOptions = [null, "inherit", "inherit"];
if (!this.options.terminalCommands) {
Expand All @@ -51,6 +65,7 @@ export class Supervisor extends EventEmitter {
WDS_EXTENSIONS: this.project.config.extensions.join(","),
},
stdio: stdio,
detached: true,
});

if (this.options.terminalCommands) {
Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ export const wds = async (options: RunOptions) => {

process.on("SIGINT", () => {
log.debug(`process ${process.pid} got SIGINT`);
project.shutdown(0);
void project.shutdown(0);
});
process.on("SIGTERM", () => {
log.debug(`process ${process.pid} got SIGTERM`);
project.shutdown(0);
void project.shutdown(0);
});

project.supervisor.process.on("exit", (code) => {
Expand All @@ -172,7 +172,7 @@ export const wds = async (options: RunOptions) => {
return;
}
logShutdown("shutting down project since it's no longer needed...");
project.shutdown(code ?? 1);
void project.shutdown(code ?? 1);
});

return server;
Expand Down

0 comments on commit d9833a5

Please sign in to comment.