diff options
author | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2016-05-15 12:23:03 -0400 |
---|---|---|
committer | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2016-05-16 14:27:13 -0400 |
commit | 2f6420ba38ab8ccaab3426c49b36484eafeb8e95 (patch) | |
tree | 57a4de7bf84f6d43ac066cd2c81fdc981366e09e | |
parent | f4f6c6e8151071d6d866603d0837d851fdf49157 (diff) | |
download | node-new-process-exit-stdio-flushing.tar.gz |
process: flush stdout/stderr upon `process.exit()`process-exit-stdio-flushing
-rw-r--r-- | deps/uv/include/uv.h | 2 | ||||
-rw-r--r-- | deps/uv/src/unix/stream.c | 15 | ||||
-rw-r--r-- | lib/internal/process.js | 62 | ||||
-rw-r--r-- | src/stream_wrap.cc | 17 | ||||
-rw-r--r-- | src/stream_wrap.h | 1 | ||||
-rw-r--r-- | test/known_issues/known_issues.status | 1 | ||||
-rw-r--r-- | test/parallel/test-stdout-buffer-flush-on-exit.js (renamed from test/known_issues/test-stdout-buffer-flush-on-exit.js) | 4 |
7 files changed, 98 insertions, 4 deletions
diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index baa0b28124..fdaebc6a0c 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -483,6 +483,8 @@ UV_EXTERN int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); +UV_EXTERN int uv_flush_sync(uv_stream_t* stream); + /* uv_write_t is a subclass of uv_req_t. */ struct uv_write_s { UV_REQ_FIELDS diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 9043664dfc..7172ad4dd5 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -1625,6 +1625,21 @@ void uv__stream_close(uv_stream_t* handle) { } +/* Have stream block and then synchronously flush queued writes. + * This function works without an event loop. + * Intended to be used just prior to exit(). + * Returns 0 on success, non-zero on failure. + */ +int uv_flush_sync(uv_stream_t* stream) { + int rc = uv_stream_set_blocking(stream, 1); + if (rc == 0) { + uv__write(stream); + rc = (int)stream->write_queue_size; + } + return rc; +} + + int uv_stream_set_blocking(uv_stream_t* handle, int blocking) { /* Don't need to check the file descriptor, uv__nonblock() * will fail with EBADF if it's not valid. diff --git a/lib/internal/process.js b/lib/internal/process.js index c435c2e871..5b6e56fa9a 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -145,7 +145,69 @@ function setupKillAndExit() { process._exiting = true; process.emit('exit', process.exitCode || 0); } + + // Flush stdio streams prior to exit. + // `flushSync` not present if stream redirected to file in shell. + flushSync(process.stdout); + flushSync(process.stderr); + process.reallyExit(process.exitCode || 0); + + function flushSync(stream) { + + // Behavior of this function outside of process.exit() is undefined + // due to the following factors: + // * Stream fd may be blocking after this call. + // * In the event of an incomplete flush pending buffered write + // requests may be truncated. + // * No return code. + + if (stream._writev) + return; + + var handle = stream._handle; + if (!handle || !handle.flushSync) + return; + + var fd = handle.fd; + if (typeof fd !== 'number' || fd < 0) + return; + + // FIXME: late module resolution avoids cross require problem + const fs = require('fs'); + + const Buffer = require('buffer'); + + // Queued libuv writes must be flushed first. + // Note: fd will set to blocking after handle.flushSync() + if (handle.flushSync() !== 0) { + // bad fd or write queue for libuv stream not entirely flushed + return; + } + + // then the queued stream chunks can be flushed + var state = stream._writableState; + var entry = state.bufferedRequest; + while (entry) { + var chunk = entry.chunk; + if (!(chunk instanceof Buffer)) { + chunk = Buffer.from(chunk, entry.encoding); + } + // Note: fd is blocking at this point + var written = fs.writeSync(fd, chunk, 0, chunk.length); + if (written !== chunk.length) { + // stream chunk not flushed entirely - stop writing. + // FIXME: buffered request queue should be repaired here + // rather than being truncated after loop break + break; + } + entry = entry.next; + } + + state.bufferedRequestCount = 0; + state.bufferedRequest = null; + state.lastBufferedRequest = null; + } }; process.kill = function(pid, sig) { diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 695e917e02..10a7110812 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -77,6 +77,7 @@ StreamWrap::StreamWrap(Environment* env, void StreamWrap::AddMethods(Environment* env, v8::Local<v8::FunctionTemplate> target, int flags) { + env->SetProtoMethod(target, "flushSync", FlushSync); env->SetProtoMethod(target, "setBlocking", SetBlocking); StreamBase::AddMethods<StreamWrap>(env, target, flags); } @@ -273,6 +274,22 @@ void StreamWrap::SetBlocking(const FunctionCallbackInfo<Value>& args) { } +void StreamWrap::FlushSync(const FunctionCallbackInfo<Value>& args) { + StreamWrap* wrap = Unwrap<StreamWrap>(args.Holder()); + + if (!wrap->IsAlive()) + return args.GetReturnValue().Set(UV_EINVAL); + +#if defined(_WIN32) + int rc = 0; +#else + int rc = uv_flush_sync(wrap->stream()); +#endif + + args.GetReturnValue().Set(rc); +} + + int StreamWrap::DoShutdown(ShutdownWrap* req_wrap) { int err; err = uv_shutdown(&req_wrap->req_, stream(), AfterShutdown); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index b0d9986db5..9c60a6c755 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -72,6 +72,7 @@ class StreamWrap : public HandleWrap, public StreamBase { int flags = StreamBase::kFlagNone); private: + static void FlushSync(const v8::FunctionCallbackInfo<v8::Value>& args); static void SetBlocking(const v8::FunctionCallbackInfo<v8::Value>& args); // Callbacks for libuv diff --git a/test/known_issues/known_issues.status b/test/known_issues/known_issues.status index 12e4b10d06..e21913e232 100644 --- a/test/known_issues/known_issues.status +++ b/test/known_issues/known_issues.status @@ -7,7 +7,6 @@ prefix known_issues [true] # This section applies to all platforms [$system==win32] -test-stdout-buffer-flush-on-exit: SKIP [$system==linux] diff --git a/test/known_issues/test-stdout-buffer-flush-on-exit.js b/test/parallel/test-stdout-buffer-flush-on-exit.js index 709928693e..791a9ccd15 100644 --- a/test/known_issues/test-stdout-buffer-flush-on-exit.js +++ b/test/parallel/test-stdout-buffer-flush-on-exit.js @@ -15,7 +15,7 @@ if (process.argv[2] === 'child') { process.exit(); } -[22, 21, 20, 19, 18, 17, 16, 16, 17, 18, 19, 20, 21, 22].forEach((exponent) => { +[22, 16].forEach((exponent) => { const bigNum = Math.pow(2, exponent); const longLine = lineSeed.repeat(bigNum); const cmd = `${process.execPath} ${__filename} child ${exponent} ${bigNum}`; @@ -23,5 +23,3 @@ if (process.argv[2] === 'child') { assert.strictEqual(stdout, longLine, `failed with exponent ${exponent}`); }); - - |