Skip to content

Commit 3c38e87

Browse files
committed
child_process: emit .send() errors in next tick
This changes the behaviour of error events emitted when process.send() fails, from synchronously to asynchronously.
1 parent a9c2791 commit 3c38e87

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

lib/internal/child_process.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ function setupChannel(target, channel) {
524524
if (typeof callback === 'function') {
525525
process.nextTick(callback, ex);
526526
} else {
527-
this.emit('error', ex); // FIXME(bnoordhuis) Defer to next tick.
527+
process.nextTick(() => this.emit('error', ex));
528528
}
529529
return false;
530530
};
@@ -635,7 +635,7 @@ function setupChannel(target, channel) {
635635
if (typeof callback === 'function') {
636636
process.nextTick(callback, ex);
637637
} else {
638-
this.emit('error', ex); // FIXME(bnoordhuis) Defer to next tick.
638+
process.nextTick(() => this.emit('error', ex));
639639
}
640640
}
641641
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const assert = require('assert');
6+
const fork = require('child_process').fork;
7+
const Pipe = process.binding('pipe_wrap').Pipe;
8+
9+
const NODE_CHANNEL_FD = 3; // equals file descriptor id of IPC created
10+
// by parent process and passed down to
11+
// child proc with process.env.NODE_CHANNEL_FD
12+
13+
if (process.argv[2] !== 'child') {
14+
const child = fork(__filename, ['child'], {
15+
execArgv: ['--expose-internals']
16+
});
17+
18+
child.on('exit', (exitCode) => process.exit(exitCode));
19+
20+
return;
21+
}
22+
23+
let errorsEmitted = 0;
24+
25+
const errorSpy = (err) => {
26+
if (err && err.message === 'channel closed') {
27+
return errorsEmitted++;
28+
}
29+
30+
throw new Error('This callback should have been called with an error!');
31+
};
32+
33+
const setupChannel = require('internal/child_process').setupChannel;
34+
35+
const channel = new Pipe(true);
36+
channel.open(NODE_CHANNEL_FD);
37+
channel.unref();
38+
setupChannel(process, channel);
39+
40+
// disconnecting the "target" will trigger errors on .send()
41+
process.connected = false;
42+
43+
process.send('A message to send..', errorSpy);
44+
assert.equal(errorsEmitted, 0,
45+
'Waits until next tick before invoking .send() callback');
46+
47+
process.nextTick(() => {
48+
assert.equal(errorsEmitted, 1);
49+
50+
process.on('error', errorSpy);
51+
process.send('A message to send..');
52+
assert.equal(errorsEmitted, 1,
53+
'Waits until next tick before emitting error');
54+
55+
process.nextTick(() => {
56+
assert.equal(errorsEmitted, 2);
57+
});
58+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const assert = require('assert');
6+
const fork = require('child_process').fork;
7+
const Pipe = process.binding('pipe_wrap').Pipe;
8+
9+
const UV_UNKNOWN_ERR = -4094;
10+
const NODE_CHANNEL_FD = 3; // equals file descriptor id of IPC created
11+
// by parent process and passed down to
12+
// child proc with process.env.NODE_CHANNEL_FD
13+
14+
if (process.argv[2] !== 'child') {
15+
const child = fork(__filename, ['child'], {
16+
execArgv: ['--expose-internals']
17+
});
18+
19+
child.on('exit', (exitCode) => process.exit(exitCode));
20+
return;
21+
}
22+
23+
let errorsEmitted = 0;
24+
25+
const errorSpy = (err) => {
26+
// UV_UNKNOWN error triggered by .writeUtf8String() below
27+
if (err && err.code === 'UNKNOWN') {
28+
return errorsEmitted++;
29+
}
30+
31+
throw new Error('This callback should have been called with an error!');
32+
};
33+
34+
const setupChannel = require('internal/child_process').setupChannel;
35+
36+
const channel = new Pipe(true);
37+
channel.open(NODE_CHANNEL_FD);
38+
channel.unref();
39+
setupChannel(process, channel);
40+
41+
// fake UV_UNKNOWN error
42+
channel.writeUtf8String = () => UV_UNKNOWN_ERR;
43+
44+
process._send('A message to send..', undefined, false, errorSpy);
45+
assert.equal(errorsEmitted, 0,
46+
'Waits until next tick before invoking ._send() callback');
47+
48+
process.nextTick(() => {
49+
assert.equal(errorsEmitted, 1);
50+
51+
process.on('error', errorSpy);
52+
process.send('A message to send..');
53+
assert.equal(errorsEmitted, 1,
54+
'Waits until next tick before emitting error');
55+
56+
process.nextTick(() => {
57+
assert.equal(errorsEmitted, 2);
58+
});
59+
});

0 commit comments

Comments
 (0)