child_process: fix data loss with readable event
authorBrian White <mscdex@mscdex.net>
Tue, 2 Feb 2016 05:57:24 +0000 (00:57 -0500)
committerMyles Borins <mborins@us.ibm.com>
Wed, 2 Mar 2016 22:01:11 +0000 (14:01 -0800)
This commit prevents child process stdio streams from being
automatically flushed on child process exit/close if a 'readable'
event handler has been attached at the time of exit.

Without this, child process stdio data can be lost if the process
exits quickly and a `read()` (e.g. from a 'readable' handler)
hasn't had the chance to get called yet.

Fixes: https://github.com/nodejs/node/issues/5034
PR-URL: https://github.com/nodejs/node/pull/5036
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
lib/internal/child_process.js
test/parallel/test-child-process-flush-stdio.js

index a6f9ed9..f482d85 100644 (file)
@@ -217,7 +217,7 @@ util.inherits(ChildProcess, EventEmitter);
 function flushStdio(subprocess) {
   if (subprocess.stdio == null) return;
   subprocess.stdio.forEach(function(stream, fd, stdio) {
-    if (!stream || !stream.readable)
+    if (!stream || !stream.readable || stream._readableState.readableListening)
       return;
     stream.resume();
   });
index 5fd7eb3..dacc122 100644 (file)
@@ -8,10 +8,22 @@ const p = cp.spawn('echo');
 p.on('close', common.mustCall(function(code, signal) {
   assert.strictEqual(code, 0);
   assert.strictEqual(signal, null);
+  spawnWithReadable();
 }));
 
 p.stdout.read();
 
-setTimeout(function() {
-  p.kill();
-}, 100);
+function spawnWithReadable() {
+  const buffer = [];
+  const p = cp.spawn('echo', ['123']);
+  p.on('close', common.mustCall(function(code, signal) {
+    assert.strictEqual(code, 0);
+    assert.strictEqual(signal, null);
+    assert.strictEqual(Buffer.concat(buffer).toString().trim(), '123');
+  }));
+  p.stdout.on('readable', function() {
+    let buf;
+    while (buf = this.read())
+      buffer.push(buf);
+  });
+}