Skip to content

Commit 3bcc9c9

Browse files
authored
Remove signal event listeners from process on finish (#512)
1 parent 79b3290 commit 3bcc9c9

File tree

2 files changed

+21
-10
lines changed

2 files changed

+21
-10
lines changed

src/flow-control/kill-on-signal.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ it('returns commands that map SIGINT to exit code 0', () => {
3232

3333
const callback = jest.fn();
3434
newCommands[0].close.subscribe(callback);
35-
process.emit('SIGINT');
35+
process.emit('SIGINT', 'SIGINT');
3636

3737
// A fake command's .kill() call won't trigger a close event automatically...
3838
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
@@ -56,7 +56,7 @@ it('returns commands that keep non-SIGINT exit codes', () => {
5656
describe.each(['SIGINT', 'SIGTERM', 'SIGHUP'])('on %s', (signal) => {
5757
it('kills all commands', () => {
5858
controller.handle(commands);
59-
process.emit(signal);
59+
process.emit(signal, signal);
6060

6161
expect(process.listenerCount(signal)).toBe(1);
6262
expect(commands[0].kill).toHaveBeenCalledWith(signal);
@@ -65,8 +65,14 @@ describe.each(['SIGINT', 'SIGTERM', 'SIGHUP'])('on %s', (signal) => {
6565

6666
it('sends abort signal', () => {
6767
controller.handle(commands);
68-
process.emit(signal);
68+
process.emit(signal, signal);
6969

7070
expect(abortController.signal.aborted).toBe(true);
7171
});
72+
73+
it('removes event listener on finish', () => {
74+
const { onFinish } = controller.handle(commands);
75+
onFinish();
76+
expect(process.listenerCount(signal)).toBe(0);
77+
});
7278
});

src/flow-control/kill-on-signal.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { map } from 'rxjs/operators';
44
import { Command } from '../command';
55
import { FlowController } from './flow-controller';
66

7+
const SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP'] as const;
8+
79
/**
810
* Watches the main concurrently process for signals and sends the same signal down to each spawned
911
* command.
@@ -25,13 +27,12 @@ export class KillOnSignal implements FlowController {
2527

2628
handle(commands: Command[]) {
2729
let caughtSignal: NodeJS.Signals;
28-
(['SIGINT', 'SIGTERM', 'SIGHUP'] as NodeJS.Signals[]).forEach((signal) => {
29-
this.process.on(signal, () => {
30-
caughtSignal = signal;
31-
this.abortController?.abort();
32-
commands.forEach((command) => command.kill(signal));
33-
});
34-
});
30+
const signalListener = (signal: NodeJS.Signals) => {
31+
caughtSignal = signal;
32+
this.abortController?.abort();
33+
commands.forEach((command) => command.kill(signal));
34+
};
35+
SIGNALS.forEach((signal) => this.process.on(signal, signalListener));
3536

3637
return {
3738
commands: commands.map((command) => {
@@ -50,6 +51,10 @@ export class KillOnSignal implements FlowController {
5051
},
5152
});
5253
}),
54+
onFinish: () => {
55+
// Avoids MaxListenersExceededWarning when running programmatically
56+
SIGNALS.forEach((signal) => this.process.off(signal, signalListener));
57+
},
5358
};
5459
}
5560
}

0 commit comments

Comments
 (0)