Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions doc/api/child_process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1714,10 +1714,14 @@ may not actually terminate the process.

See kill(2) for reference.

On Windows, where POSIX signals do not exist, the `signal` argument will be
ignored except for `'SIGKILL'`, `'SIGTERM'`, `'SIGINT'` and `'SIGQUIT'`, and the
process will always be killed forcefully and abruptly (similar to `'SIGKILL'`).
See [Signal Events][] for more details.
On Windows, where POSIX signals do not exist, signals are handled as follows.
`'SIGKILL'`, `'SIGTERM'`, `'SIGINT'` and `'SIGQUIT'` terminate the process
forcefully and abruptly (similar to `'SIGKILL'`); any other signal whose name is
known on Windows (such as `'SIGHUP'`) does the same. `'SIGWINCH'` is not
terminal and is not coerced: `subprocess.kill()` throws an `ENOSYS` error and
the child keeps running. A signal name that does not exist on Windows (such as
`'SIGSTOP'`) throws an `ERR_UNKNOWN_SIGNAL` error. See [Signal Events][] for more
details.

On Linux, child processes of child processes will not be terminated
when attempting to kill their parent. This is likely to happen when running a
Expand Down
2 changes: 1 addition & 1 deletion src/process_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ class ProcessWrap : public HandleWrap {
}
#ifdef _WIN32
if (signal != SIGKILL && signal != SIGTERM && signal != SIGINT &&
signal != SIGQUIT && signal != 0) {
signal != SIGQUIT && signal != 0 && signal != SIGWINCH) {
signal = SIGKILL;
}
#endif
Expand Down
34 changes: 34 additions & 0 deletions test/parallel/test-child-process-kill-sigwinch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { spawn } = require('child_process');

// SIGWINCH is a non-terminal signal: sending it must not terminate the target
// process. On Windows, kill() must surface ENOSYS instead of coercing
// SIGWINCH into a SIGKILL (which would wrongly terminate the process).
// Refs: https://github.com/nodejs/node/issues/64324

const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)']);

// The process must survive the SIGWINCH; it is only ever torn down by the
// explicit SIGKILL in the cleanup below (after the listener is removed).
child.on('exit', common.mustNotCall('child must survive SIGWINCH'));

child.on('spawn', common.mustCall(() => {
if (common.isWindows) {
assert.throws(() => child.kill('SIGWINCH'), { code: 'ENOSYS' });
} else {
assert.strictEqual(child.kill('SIGWINCH'), true);
}

assert.strictEqual(child.signalCode, null);
assert.strictEqual(child.exitCode, null);

setTimeout(common.mustCall(() => {
assert.strictEqual(child.signalCode, null);
assert.strictEqual(child.exitCode, null);

child.removeAllListeners('exit');
child.kill('SIGKILL');
}), common.platformTimeout(500));
}));
Loading