Skip to content

Commit 1fea458

Browse files
committed
lib: fix stdio/ipc sync i/o regression
process.send() should be synchronous, it should block until the message has been sent in full, but it wasn't after the second-to-last libuv upgrade because of commit libuv/libuv@393c1c5 ("unix: set non-block mode in uv_{pipe,tcp,udp}_open"), which made its way into io.js in commit 07bd05b ("deps: update libuv to 1.2.1"). Commit libuv/libuv@b36d4ff ("unix: implement uv_stream_set_blocking()") as landed in io.js in commit 9681fca ("deps: update libuv to 1.4.0") makes it possible to restore the synchronous behavior again and that's precisely what this commit does. The same line of reasoning applies to `net.Socket({ fd: 1 })`: creating a socket object from a stdio file descriptor, like the `process.stdout` getter does, should put the file descriptor in blocking mode for compatibility reasons. Fixes: #760 Fixes: #784
1 parent aea9b89 commit 1fea458

File tree

4 files changed

+56
-4
lines changed

4 files changed

+56
-4
lines changed

lib/child_process.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,13 @@ exports._forkChild = function(fd) {
566566
// set process.send()
567567
var p = createPipe(true);
568568
p.open(fd);
569+
570+
// p.open() puts the file descriptor in non-blocking mode
571+
// but it must be synchronous for backwards compatibility.
572+
var err = p.setBlocking(true);
573+
if (err)
574+
throw errnoException(err, 'setBlocking');
575+
569576
p.unref();
570577
setupChannel(process, p);
571578

lib/net.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,9 @@ function Socket(options) {
134134
} else if (options.fd !== undefined) {
135135
this._handle = createHandle(options.fd);
136136
this._handle.open(options.fd);
137-
if ((options.fd == 1 || options.fd == 2) &&
138-
(this._handle instanceof Pipe) &&
139-
process.platform === 'win32') {
140-
// Make stdout and stderr blocking on Windows
137+
// this._handle.open() puts the file descriptor in non-blocking
138+
// mode but it must be synchronous for backwards compatibility.
139+
if ((options.fd == 1 || options.fd == 2) && this._handle instanceof Pipe) {
141140
var err = this._handle.setBlocking(true);
142141
if (err)
143142
throw errnoException(err, 'setBlocking');
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var common = require('../common');
2+
var assert = require('assert');
3+
var fork = require('child_process').fork;
4+
var N = 4 << 20; // 4 MB
5+
6+
for (var big = '*'; big.length < N; big += big);
7+
8+
if (process.argv[2] === 'child') {
9+
process.send(big);
10+
process.exit(42);
11+
}
12+
13+
var proc = fork(__filename, ['child']);
14+
15+
proc.on('message', common.mustCall(function(msg) {
16+
assert.equal(typeof msg, 'string');
17+
assert.equal(msg.length, N);
18+
assert.equal(msg, big);
19+
}));
20+
21+
proc.on('exit', common.mustCall(function(exitCode) {
22+
assert.equal(exitCode, 42);
23+
}));
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var common = require('../common');
2+
var assert = require('assert');
3+
var spawn = require('child_process').spawn;
4+
var N = 4 << 20; // 4 MB
5+
6+
for (var big = '*'; big.length < N; big += big);
7+
8+
if (process.argv[2] === 'child') {
9+
process.stdout.write(big);
10+
process.exit(42);
11+
}
12+
13+
var stdio = ['inherit', 'pipe', 'inherit'];
14+
var proc = spawn(process.execPath, [__filename, 'child'], { stdio: stdio });
15+
16+
var chunks = [];
17+
proc.stdout.setEncoding('utf8');
18+
proc.stdout.on('data', chunks.push.bind(chunks));
19+
20+
proc.on('exit', common.mustCall(function(exitCode) {
21+
assert.equal(exitCode, 42);
22+
assert.equal(chunks.join(''), big);
23+
}));

0 commit comments

Comments
 (0)