Skip to content

Commit 727bea2

Browse files
committed
Allow explicitely kill process
1 parent 6a91984 commit 727bea2

File tree

3 files changed

+74
-63
lines changed

3 files changed

+74
-63
lines changed

process/src/exec/api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export interface Process extends StdIO {
2424
* not complete successfully, it will raise an ExecError.
2525
*/
2626
expect(): Operation<ExitStatus>;
27+
28+
/**
29+
* Kill the child process
30+
*/
31+
kill(): Operation<void>;
2732
}
2833

2934
export interface ExecOptions {
@@ -77,6 +82,7 @@ export interface ProcessResult extends ExitStatus {
7782
stdout: string;
7883
stderr: string;
7984
}
85+
8086
export interface CreateOSProcess {
8187
(command: string, options: ExecOptions): Operation<Process>;
8288
}

process/src/exec/posix.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,21 @@ export const createPosixProcess: CreateOSProcess = function* createPosixProcess(
9090
processResult.resolve(Ok(value));
9191
} finally {
9292
try {
93-
if (typeof childProcess.pid === "undefined") {
94-
// deno-lint-ignore no-unsafe-finally
95-
throw new Error("no pid for childProcess");
96-
}
97-
process.kill(-childProcess.pid, "SIGTERM");
98-
yield* all([io.stdoutDone.operation, io.stderrDone.operation]);
93+
yield* kill();
9994
} catch (_e) {
10095
// do nothing, process is probably already dead
10196
}
10297
}
10398
});
10499

100+
function* kill() {
101+
if (typeof childProcess.pid === "undefined") {
102+
throw new Error("no pid for childProcess");
103+
}
104+
process.kill(-childProcess.pid, "SIGTERM");
105+
yield* all([io.stdoutDone.operation, io.stderrDone.operation]);
106+
}
107+
105108
function* join() {
106109
let result = yield* processResult.operation;
107110
if (result.ok) {
@@ -129,5 +132,6 @@ export const createPosixProcess: CreateOSProcess = function* createPosixProcess(
129132
stderr,
130133
join,
131134
expect,
135+
kill,
132136
};
133137
};

process/src/exec/win32.ts

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -110,74 +110,74 @@ export const createWin32Process: CreateOSProcess = function* createWin32Process(
110110
processResult.resolve(Ok(value));
111111
} finally {
112112
try {
113-
// Only try to kill the process if it hasn't exited yet
114-
if (
115-
childProcess.exitCode === null &&
116-
childProcess.signalCode === null
117-
) {
118-
if (typeof childProcess.pid === "undefined") {
119-
// deno-lint-ignore no-unsafe-finally
120-
throw new Error("no pid for childProcess");
121-
}
113+
yield* kill();
114+
} catch (_e) {
115+
// do nothing, process is probably already dead
116+
}
117+
}
118+
});
122119

123-
let stdinStream = childProcess.stdin;
120+
function* kill() {
121+
// Only try to kill the process if it hasn't exited yet
122+
if (
123+
childProcess.exitCode === null &&
124+
childProcess.signalCode === null
125+
) {
126+
if (typeof childProcess.pid === "undefined") {
127+
throw new Error("no pid for childProcess");
128+
}
124129

125-
// Try graceful shutdown with ctrlc
126-
try {
127-
ctrlc(childProcess.pid);
128-
if (stdinStream.writable) {
129-
try {
130-
// Terminate batch process (Y/N)
131-
stdinStream.write("Y\n");
132-
} catch (_err) {
133-
// not much we can do here
134-
}
135-
}
136-
} catch (_err) {
137-
// ctrlc might fail
138-
}
130+
let stdinStream = childProcess.stdin;
139131

140-
// Close stdin to allow process to exit cleanly
132+
// Try graceful shutdown with ctrlc
133+
try {
134+
ctrlc(childProcess.pid);
135+
if (stdinStream.writable) {
141136
try {
142-
stdinStream.end();
137+
// Terminate batch process (Y/N)
138+
stdinStream.write("Y\n");
143139
} catch (_err) {
144-
// stdin might already be closed
140+
// not much we can do here
145141
}
142+
}
143+
} catch (_err) {
144+
// ctrlc might fail
145+
}
146146

147-
// Wait for graceful exit with a timeout
148-
yield* race([processResult.operation, sleep(300)]);
149-
150-
// If process still hasn't exited, escalate
151-
if (
152-
childProcess.exitCode === null &&
153-
childProcess.signalCode === null
154-
) {
155-
// Try regular kill first
156-
try {
157-
childProcess.kill();
158-
} catch (_err) {
159-
// process might already be dead
160-
}
161-
162-
// If still alive after kill, force-kill entire process tree
163-
// This is necessary for bash on Windows where ctrlc doesn't work
164-
// and child.kill() only kills the shell, leaving grandchildren alive
165-
if (
166-
childProcess.exitCode === null &&
167-
childProcess.signalCode === null
168-
) {
169-
yield* killTree(childProcess.pid);
170-
}
171-
}
147+
// Close stdin to allow process to exit cleanly
148+
try {
149+
stdinStream.end();
150+
} catch (_err) {
151+
// stdin might already be closed
152+
}
172153

173-
// Wait for streams to finish
174-
yield* all([io.stdoutDone.operation, io.stderrDone.operation]);
154+
// If process still hasn't exited, escalate
155+
if (
156+
childProcess.exitCode === null &&
157+
childProcess.signalCode === null
158+
) {
159+
// Try regular kill first
160+
try {
161+
childProcess.kill();
162+
} catch (_err) {
163+
// process might already be dead
164+
}
165+
166+
// If still alive after kill, force-kill entire process tree
167+
// This is necessary for bash on Windows where ctrlc doesn't work
168+
// and child.kill() only kills the shell, leaving grandchildren alive
169+
if (
170+
childProcess.exitCode === null &&
171+
childProcess.signalCode === null
172+
) {
173+
yield* killTree(childProcess.pid);
175174
}
176-
} catch (_e) {
177-
// do nothing, process is probably already dead
178175
}
176+
177+
// Wait for streams to finish
178+
yield* all([io.stdoutDone.operation, io.stderrDone.operation]);
179179
}
180-
});
180+
}
181181

182182
function* join() {
183183
let result = yield* processResult.operation;
@@ -206,6 +206,7 @@ export const createWin32Process: CreateOSProcess = function* createWin32Process(
206206
stderr,
207207
join,
208208
expect,
209+
kill,
209210
};
210211
};
211212

0 commit comments

Comments
 (0)