Browse Source

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.

Reviewed-By: Julien Gilli <julien.gilli@joyent.com>
PR-URL: https://github.com/joyent/node/pull/9179
v0.12.2-release
Ben Noordhuis 10 years ago
committed by Trevor Norris
parent
commit
2411bea0df
  1. 7
      lib/child_process.js
  2. 6
      lib/net.js
  3. 44
      test/simple/test-child-process-sync-process-send.js
  4. 44
      test/simple/test-net-sync-stdio.js

7
lib/child_process.js

@ -586,6 +586,13 @@ exports._forkChild = function(fd) {
// set process.send()
var p = createPipe(true);
p.open(fd);
// p.open() puts the file descriptor in non-blocking mode
// but it must be synchronous for backwards compatibility.
var err = p.setBlocking(true);
if (err)
throw errnoException(err, 'setBlocking');
p.unref();
setupChannel(process, p);

6
lib/net.js

@ -155,9 +155,9 @@ function Socket(options) {
} else if (!util.isUndefined(options.fd)) {
this._handle = createHandle(options.fd);
this._handle.open(options.fd);
if ((options.fd == 1 || options.fd == 2) &&
(this._handle instanceof Pipe) &&
process.platform === 'win32') {
// this._handle.open() puts the file descriptor in non-blocking
// mode but it must be synchronous for backwards compatibility.
if ((options.fd == 1 || options.fd == 2) && this._handle instanceof Pipe) {
// Make stdout and stderr blocking on Windows
var err = this._handle.setBlocking(true);
if (err)

44
test/simple/test-child-process-sync-process-send.js

@ -0,0 +1,44 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var fork = require('child_process').fork;
var N = 4 << 20; // 4 MB
for (var big = '*'; big.length < N; big += big);
if (process.argv[2] === 'child') {
process.send(big);
process.exit(42);
}
var proc = fork(__filename, ['child']);
proc.on('message', common.mustCall(function(msg) {
assert.equal(typeof msg, 'string');
assert.equal(msg.length, N);
assert.equal(msg, big);
}));
proc.on('exit', common.mustCall(function(exitCode) {
assert.equal(exitCode, 42);
}));

44
test/simple/test-net-sync-stdio.js

@ -0,0 +1,44 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var spawn = require('child_process').spawn;
var N = 4 << 20; // 4 MB
for (var big = '*'; big.length < N; big += big);
if (process.argv[2] === 'child') {
process.stdout.write(big);
process.exit(42);
}
var stdio = ['inherit', 'pipe', 'inherit'];
var proc = spawn(process.execPath, [__filename, 'child'], { stdio: stdio });
var chunks = [];
proc.stdout.setEncoding('utf8');
proc.stdout.on('data', chunks.push.bind(chunks));
proc.on('exit', common.mustCall(function(exitCode) {
assert.equal(exitCode, 42);
assert.equal(chunks.join(''), big);
}));
Loading…
Cancel
Save