Skip to content

Commit d3f4df5

Browse files
benjamingrtargos
authored andcommitted
child_process: clean event listener correctly
I was working on AbortSignal for spawn and noticed there is a leak in the current code for AbortSignal support in child_process since it removes the wrong listener. I used the new signal as argument feature to make removing the listener easier and added a test. PR-URL: nodejs#36424 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 68bcd8d commit d3f4df5

File tree

2 files changed

+15
-5
lines changed

2 files changed

+15
-5
lines changed

lib/child_process.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,13 @@ function execFile(file /* , args, options, callback */) {
365365
if (options.signal.aborted) {
366366
process.nextTick(() => kill());
367367
} else {
368+
const childController = new AbortController();
368369
options.signal.addEventListener('abort', () => {
369-
if (!ex) {
370+
if (!ex)
370371
ex = new AbortError();
371-
}
372372
kill();
373-
});
374-
const remove = () => options.signal.removeEventListener('abort', kill);
375-
child.once('close', remove);
373+
}, { signal: childController.signal });
374+
child.once('close', () => childController.abort());
376375
}
377376
}
378377

test/parallel/test-child-process-execfile.js

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../common');
55
const assert = require('assert');
66
const execFile = require('child_process').execFile;
7+
const { getEventListeners } = require('events');
78
const { getSystemErrorName } = require('util');
89
const fixtures = require('../common/fixtures');
910

@@ -69,5 +70,15 @@ const execOpts = { encoding: 'utf8', shell: true };
6970

7071
execFile(process.execPath, [echoFixture, 0], { signal: 'hello' }, callback);
7172
}, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' });
73+
}
74+
{
75+
// Verify that the process completing removes the abort listener
76+
const ac = new AbortController();
77+
const { signal } = ac;
7278

79+
const callback = common.mustCall((err) => {
80+
assert.strictEqual(getEventListeners(ac.signal).length, 0);
81+
assert.strictEqual(err, null);
82+
});
83+
execFile(process.execPath, [fixture, 0], { signal }, callback);
7384
}

0 commit comments

Comments
 (0)