diff --git a/doc/api/child_processes.markdown b/doc/api/child_processes.markdown index a7c174c67e6..57aa9452098 100644 --- a/doc/api/child_processes.markdown +++ b/doc/api/child_processes.markdown @@ -24,6 +24,13 @@ of the signal, otherwise `null`. See `waitpid(2)`. +### Event: 'disconnect' + +This event is emitted after using the `.disconnect()` method in the parent or +in the child. After disconnecting it is no longer possible to send messages. +An alternative way to check if you can send messages is to see if the +`child.connected` property is `true`. + ### child.stdin A `Writable Stream` that represents the child process's `stdin`. @@ -264,7 +271,12 @@ processes: } }); - +To close the IPC connection between parent and child use the +`child.disconnect()` method. This allows the child to exit gracefully since +there is no IPC channel keeping it alive. When calling this method the +`disconnect` event will be emitted in both parent and child, and the +`connected` flag will be set to `false`. Please note that you can also call +`process.disconnect()` in the child process. ### child.kill([signal]) diff --git a/lib/child_process.js b/lib/child_process.js index 9cdf7d6eecb..4af4d4f4139 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -85,6 +85,7 @@ function setupChannel(target, channel) { } } + channel.buffering = false; channel.onread = function(pool, offset, length, recvHandle) { if (recvHandle && setSimultaneousAccepts) { // Update simultaneous accepts on Windows @@ -117,10 +118,11 @@ function setupChannel(target, channel) { start = i + 1; } jsonBuffer = jsonBuffer.slice(start); + this.buffering = jsonBuffer.length !== 0; } else { - channel.close(); - target._channel = null; + this.buffering = false; + target.disconnect(); } }; @@ -129,7 +131,7 @@ function setupChannel(target, channel) { throw new TypeError('message cannot be undefined'); } - if (!target._channel) throw new Error("channel closed"); + if (!this.connected) throw new Error("channel closed"); // For overflow protection don't write if channel queue is too deep. if (channel.writeQueueSize > 1024 * 1024) { @@ -154,6 +156,34 @@ function setupChannel(target, channel) { return true; }; + target.connected = true; + target.disconnect = function() { + if (!this.connected) return; + + // do not allow messages to be written + this.connected = false; + this._channel = null; + + var fired = false; + function finish() { + if (fired) return; + fired = true; + + channel.close(); + target.emit('disconnect'); + } + + // If a message is being read, then wait for it to complete. + if (channel.buffering) { + this.once('message', finish); + this.once('internalMessage', finish); + + return; + } + + finish(); + }; + channel.readStart(); } @@ -201,11 +231,8 @@ exports.fork = function(modulePath /*, args, options*/) { if (!options.thread) setupChannel(child, options.stdinStream); - child.on('exit', function() { - if (child._channel) { - child._channel.close(); - } - }); + // Disconnect when the child process exits. + child.once('exit', child.disconnect.bind(child)); return child; };