Skip to content

Commit 87d682b

Browse files
committed
child_process: emit IPC messages on next tick
This commit fixes a regression related to IPC 'message' events. When messages are not emitted in the next tick, a 'message' handler that throws can break the IPC read loop. Refs: #6909 Refs: #13459 Refs: #13648 PR-URL: #13856 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Santiago Gimeno <[email protected]>
1 parent 748b2a8 commit 87d682b

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

lib/internal/child_process.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,6 @@ function setupChannel(target, channel) {
457457
}
458458
chunks[0] = jsonBuffer + chunks[0];
459459

460-
var nextTick = false;
461460
for (var i = 0; i < numCompleteChunks; i++) {
462461
var message = JSON.parse(chunks[i]);
463462

@@ -466,12 +465,11 @@ function setupChannel(target, channel) {
466465
// that we deliver the handle with the right message however.
467466
if (isInternal(message)) {
468467
if (message.cmd === 'NODE_HANDLE')
469-
handleMessage(message, recvHandle, true, false);
468+
handleMessage(message, recvHandle, true);
470469
else
471-
handleMessage(message, undefined, true, false);
470+
handleMessage(message, undefined, true);
472471
} else {
473-
handleMessage(message, undefined, false, nextTick);
474-
nextTick = true;
472+
handleMessage(message, undefined, false);
475473
}
476474
}
477475
jsonBuffer = incompleteChunk;
@@ -533,7 +531,7 @@ function setupChannel(target, channel) {
533531

534532
// Convert handle object
535533
obj.got.call(this, message, handle, function(handle) {
536-
handleMessage(message.msg, handle, isInternal(message.msg), false);
534+
handleMessage(message.msg, handle, isInternal(message.msg));
537535
});
538536
});
539537

@@ -743,15 +741,13 @@ function setupChannel(target, channel) {
743741
target.emit(event, message, handle);
744742
}
745743

746-
function handleMessage(message, handle, internal, nextTick) {
744+
function handleMessage(message, handle, internal) {
747745
if (!target.channel)
748746
return;
749747

750748
var eventName = (internal ? 'internalMessage' : 'message');
751-
if (nextTick)
752-
process.nextTick(emit, eventName, message, handle);
753-
else
754-
target.emit(eventName, message, handle);
749+
750+
process.nextTick(emit, eventName, message, handle);
755751
}
756752

757753
channel.readStart();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const cp = require('child_process');
5+
const NUM_MESSAGES = 10;
6+
const values = [];
7+
8+
for (let i = 0; i < NUM_MESSAGES; ++i) {
9+
values[i] = i;
10+
}
11+
12+
if (process.argv[2] === 'child') {
13+
const received = values.map(() => { return false; });
14+
15+
process.on('uncaughtException', common.mustCall((err) => {
16+
received[err] = true;
17+
const done = received.every((element) => { return element === true; });
18+
19+
if (done)
20+
process.disconnect();
21+
}, NUM_MESSAGES));
22+
23+
process.on('message', (msg) => {
24+
// If messages are handled synchronously, throwing should break the IPC
25+
// message processing.
26+
throw msg;
27+
});
28+
29+
process.send('ready');
30+
} else {
31+
const child = cp.fork(__filename, ['child']);
32+
33+
child.on('message', common.mustCall((msg) => {
34+
assert.strictEqual(msg, 'ready');
35+
values.forEach((value) => {
36+
child.send(value);
37+
});
38+
}));
39+
}

0 commit comments

Comments
 (0)