diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 06daf2183db3..88096def5401 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -693,9 +693,11 @@ function setupChannel(target, channel, serializationMode) { this.buffering = false; target.disconnect(); channel.onread = nop; - channel.close(); - target.channel = null; - maybeClose(target); + if (target.channel !== null) { + channel.close(); + target.channel = null; + maybeClose(target); + } } }; @@ -986,6 +988,7 @@ function setupChannel(target, channel, serializationMode) { channel.close(); target.emit('disconnect'); + maybeClose(target); } // If a message is being read, then wait for it to complete. diff --git a/test/parallel/test-child-process-disconnect-close.js b/test/parallel/test-child-process-disconnect-close.js new file mode 100644 index 000000000000..9c968983d533 --- /dev/null +++ b/test/parallel/test-child-process-disconnect-close.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +if (process.argv[2] === 'child') { + process.on('disconnect', common.mustCall(() => { + process.exit(0); + })); + process.send('ready'); +} else { + const child = spawn(process.execPath, [__filename, 'child'], { + stdio: ['ignore', 'pipe', 'pipe', 'ipc'], + }); + + let closed = false; + + child.on('disconnect', common.mustCall(() => { + assert.strictEqual(child.connected, false); + })); + + child.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + })); + + // Regression test for https://github.com/nodejs/node/issues/65646: + // the 'close' event must be emitted after the parent calls disconnect() + // even though the channel was closed by the parent instead of reaching EOF. + child.on('close', common.mustCall((code, signal) => { + closed = true; + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + + child.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'ready'); + child.disconnect(); + })); + + process.on('exit', () => { + assert.strictEqual(closed, true); + }); +}