child_process: don't crash process on internal ops
1. Swallow errors when sending internal NODE_HANDLE_ACK messages, so they don't crash the process. 2. Queue process.disconnect() if there are any pending queued messages. Fixes test-child-process-fork-net2.js on win.
This commit is contained in:
parent
06b1945298
commit
0da4e0e843
@ -369,16 +369,25 @@ function setupChannel(target, channel) {
|
|||||||
assert(util.isArray(target._handleQueue));
|
assert(util.isArray(target._handleQueue));
|
||||||
var queue = target._handleQueue;
|
var queue = target._handleQueue;
|
||||||
target._handleQueue = null;
|
target._handleQueue = null;
|
||||||
|
|
||||||
queue.forEach(function(args) {
|
queue.forEach(function(args) {
|
||||||
target.send(args.message, args.handle);
|
target._send(args.message, args.handle, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Process a pending disconnect (if any).
|
||||||
|
if (!target.connected && target._channel && !target._handleQueue)
|
||||||
|
target._disconnect();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.cmd !== 'NODE_HANDLE') return;
|
if (message.cmd !== 'NODE_HANDLE') return;
|
||||||
|
|
||||||
// Acknowledge handle receival.
|
// Acknowledge handle receival. Don't emit error events (for example if
|
||||||
target.send({ cmd: 'NODE_HANDLE_ACK' });
|
// the other side has disconnected) because this call to send() is not
|
||||||
|
// initiated by the user and it shouldn't be fatal to be unable to ACK
|
||||||
|
// a message.
|
||||||
|
target._send({ cmd: 'NODE_HANDLE_ACK' }, null, true);
|
||||||
|
|
||||||
var obj = handleConversion[message.type];
|
var obj = handleConversion[message.type];
|
||||||
|
|
||||||
@ -395,14 +404,17 @@ function setupChannel(target, channel) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
target.send = function(message, handle) {
|
target.send = function(message, handle) {
|
||||||
if (util.isUndefined(message)) {
|
if (!this.connected)
|
||||||
throw new TypeError('message cannot be undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.connected) {
|
|
||||||
this.emit('error', new Error('channel closed'));
|
this.emit('error', new Error('channel closed'));
|
||||||
return;
|
else
|
||||||
}
|
this._send(message, handle, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
target._send = function(message, handle, swallowErrors) {
|
||||||
|
assert(this.connected || this._channel);
|
||||||
|
|
||||||
|
if (util.isUndefined(message))
|
||||||
|
throw new TypeError('message cannot be undefined');
|
||||||
|
|
||||||
// package messages with a handle object
|
// package messages with a handle object
|
||||||
if (handle) {
|
if (handle) {
|
||||||
@ -454,7 +466,8 @@ function setupChannel(target, channel) {
|
|||||||
var err = channel.writeUtf8String(req, string, handle);
|
var err = channel.writeUtf8String(req, string, handle);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
this.emit('error', errnoException(err, 'write'));
|
if (!swallowErrors)
|
||||||
|
this.emit('error', errnoException(err, 'write'));
|
||||||
} else if (handle && !this._handleQueue) {
|
} else if (handle && !this._handleQueue) {
|
||||||
this._handleQueue = [];
|
this._handleQueue = [];
|
||||||
}
|
}
|
||||||
@ -467,15 +480,37 @@ function setupChannel(target, channel) {
|
|||||||
return channel.writeQueueSize < (65536 * 2);
|
return channel.writeQueueSize < (65536 * 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// connected will be set to false immediately when a disconnect() is
|
||||||
|
// requested, even though the channel might still be alive internally to
|
||||||
|
// process queued messages. The three states are distinguished as follows:
|
||||||
|
// - disconnect() never requested: _channel is not null and connected
|
||||||
|
// is true
|
||||||
|
// - disconnect() requested, messages in the queue: _channel is not null
|
||||||
|
// and connected is false
|
||||||
|
// - disconnect() requested, channel actually disconnected: _channel is
|
||||||
|
// null and connected is false
|
||||||
target.connected = true;
|
target.connected = true;
|
||||||
|
|
||||||
target.disconnect = function() {
|
target.disconnect = function() {
|
||||||
if (!this.connected) {
|
if (!this.connected) {
|
||||||
this.emit('error', new Error('IPC channel is already disconnected'));
|
this.emit('error', new Error('IPC channel is already disconnected'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not allow messages to be written
|
// Do not allow any new messages to be written.
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
|
||||||
|
// If there are no queued messages, disconnect immediately. Otherwise,
|
||||||
|
// postpone the disconnect so that it happens internally after the
|
||||||
|
// queue is flushed.
|
||||||
|
if (!this._handleQueue)
|
||||||
|
this._disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
target._disconnect = function() {
|
||||||
|
assert(this._channel);
|
||||||
|
|
||||||
|
// This marks the fact that the channel is actually disconnected.
|
||||||
this._channel = null;
|
this._channel = null;
|
||||||
|
|
||||||
var fired = false;
|
var fired = false;
|
||||||
|
@ -52,8 +52,8 @@ if (process.argv[2] === 'child') {
|
|||||||
needEnd.push(socket);
|
needEnd.push(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on('close', function() {
|
socket.on('close', function(had_error) {
|
||||||
console.error('[%d] socket.close', id, m);
|
console.error('[%d] socket.close', id, had_error, m);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('finish', function() {
|
socket.on('finish', function() {
|
||||||
@ -65,7 +65,7 @@ if (process.argv[2] === 'child') {
|
|||||||
if (m !== 'close') return;
|
if (m !== 'close') return;
|
||||||
console.error('[%d] got close message', id);
|
console.error('[%d] got close message', id);
|
||||||
needEnd.forEach(function(endMe, i) {
|
needEnd.forEach(function(endMe, i) {
|
||||||
console.error('[%d] ending %d', id, i);
|
console.error('[%d] ending %d/%d', id, i, needEnd.length);
|
||||||
endMe.end('end');
|
endMe.end('end');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -73,7 +73,7 @@ if (process.argv[2] === 'child') {
|
|||||||
process.on('disconnect', function() {
|
process.on('disconnect', function() {
|
||||||
console.error('[%d] process disconnect, ending', id);
|
console.error('[%d] process disconnect, ending', id);
|
||||||
needEnd.forEach(function(endMe, i) {
|
needEnd.forEach(function(endMe, i) {
|
||||||
console.error('[%d] ending %d', id, i);
|
console.error('[%d] ending %d/%d', id, i, needEnd.length);
|
||||||
endMe.end('end');
|
endMe.end('end');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -120,6 +120,11 @@ if (process.argv[2] === 'child') {
|
|||||||
var j = count, client;
|
var j = count, client;
|
||||||
while (j--) {
|
while (j--) {
|
||||||
client = net.connect(common.PORT, '127.0.0.1');
|
client = net.connect(common.PORT, '127.0.0.1');
|
||||||
|
client.on('error', function() {
|
||||||
|
// This can happen if we kill the child too early.
|
||||||
|
// The client should still get a close event afterwards.
|
||||||
|
console.error('[m] CLIENT: error event');
|
||||||
|
});
|
||||||
client.on('close', function() {
|
client.on('close', function() {
|
||||||
console.error('[m] CLIENT: close event');
|
console.error('[m] CLIENT: close event');
|
||||||
disconnected += 1;
|
disconnected += 1;
|
||||||
@ -136,6 +141,7 @@ if (process.argv[2] === 'child') {
|
|||||||
console.error('[m] server close');
|
console.error('[m] server close');
|
||||||
closeEmitted = true;
|
closeEmitted = true;
|
||||||
|
|
||||||
|
console.error('[m] killing child processes');
|
||||||
child1.kill();
|
child1.kill();
|
||||||
child2.kill();
|
child2.kill();
|
||||||
child3.kill();
|
child3.kill();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user