Skip to content

Commit 2f6420b

Browse files
committed
process: flush stdout/stderr upon process.exit()
1 parent f4f6c6e commit 2f6420b

File tree

7 files changed

+98
-4
lines changed

7 files changed

+98
-4
lines changed

deps/uv/include/uv.h

+2
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ UV_EXTERN int uv_try_write(uv_stream_t* handle,
483483
const uv_buf_t bufs[],
484484
unsigned int nbufs);
485485

486+
UV_EXTERN int uv_flush_sync(uv_stream_t* stream);
487+
486488
/* uv_write_t is a subclass of uv_req_t. */
487489
struct uv_write_s {
488490
UV_REQ_FIELDS

deps/uv/src/unix/stream.c

+15
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,21 @@ void uv__stream_close(uv_stream_t* handle) {
16251625
}
16261626

16271627

1628+
/* Have stream block and then synchronously flush queued writes.
1629+
* This function works without an event loop.
1630+
* Intended to be used just prior to exit().
1631+
* Returns 0 on success, non-zero on failure.
1632+
*/
1633+
int uv_flush_sync(uv_stream_t* stream) {
1634+
int rc = uv_stream_set_blocking(stream, 1);
1635+
if (rc == 0) {
1636+
uv__write(stream);
1637+
rc = (int)stream->write_queue_size;
1638+
}
1639+
return rc;
1640+
}
1641+
1642+
16281643
int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
16291644
/* Don't need to check the file descriptor, uv__nonblock()
16301645
* will fail with EBADF if it's not valid.

lib/internal/process.js

+62
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,69 @@ function setupKillAndExit() {
145145
process._exiting = true;
146146
process.emit('exit', process.exitCode || 0);
147147
}
148+
149+
// Flush stdio streams prior to exit.
150+
// `flushSync` not present if stream redirected to file in shell.
151+
flushSync(process.stdout);
152+
flushSync(process.stderr);
153+
148154
process.reallyExit(process.exitCode || 0);
155+
156+
function flushSync(stream) {
157+
158+
// Behavior of this function outside of process.exit() is undefined
159+
// due to the following factors:
160+
// * Stream fd may be blocking after this call.
161+
// * In the event of an incomplete flush pending buffered write
162+
// requests may be truncated.
163+
// * No return code.
164+
165+
if (stream._writev)
166+
return;
167+
168+
var handle = stream._handle;
169+
if (!handle || !handle.flushSync)
170+
return;
171+
172+
var fd = handle.fd;
173+
if (typeof fd !== 'number' || fd < 0)
174+
return;
175+
176+
// FIXME: late module resolution avoids cross require problem
177+
const fs = require('fs');
178+
179+
const Buffer = require('buffer');
180+
181+
// Queued libuv writes must be flushed first.
182+
// Note: fd will set to blocking after handle.flushSync()
183+
if (handle.flushSync() !== 0) {
184+
// bad fd or write queue for libuv stream not entirely flushed
185+
return;
186+
}
187+
188+
// then the queued stream chunks can be flushed
189+
var state = stream._writableState;
190+
var entry = state.bufferedRequest;
191+
while (entry) {
192+
var chunk = entry.chunk;
193+
if (!(chunk instanceof Buffer)) {
194+
chunk = Buffer.from(chunk, entry.encoding);
195+
}
196+
// Note: fd is blocking at this point
197+
var written = fs.writeSync(fd, chunk, 0, chunk.length);
198+
if (written !== chunk.length) {
199+
// stream chunk not flushed entirely - stop writing.
200+
// FIXME: buffered request queue should be repaired here
201+
// rather than being truncated after loop break
202+
break;
203+
}
204+
entry = entry.next;
205+
}
206+
207+
state.bufferedRequestCount = 0;
208+
state.bufferedRequest = null;
209+
state.lastBufferedRequest = null;
210+
}
149211
};
150212

151213
process.kill = function(pid, sig) {

src/stream_wrap.cc

+17
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ StreamWrap::StreamWrap(Environment* env,
7777
void StreamWrap::AddMethods(Environment* env,
7878
v8::Local<v8::FunctionTemplate> target,
7979
int flags) {
80+
env->SetProtoMethod(target, "flushSync", FlushSync);
8081
env->SetProtoMethod(target, "setBlocking", SetBlocking);
8182
StreamBase::AddMethods<StreamWrap>(env, target, flags);
8283
}
@@ -273,6 +274,22 @@ void StreamWrap::SetBlocking(const FunctionCallbackInfo<Value>& args) {
273274
}
274275

275276

277+
void StreamWrap::FlushSync(const FunctionCallbackInfo<Value>& args) {
278+
StreamWrap* wrap = Unwrap<StreamWrap>(args.Holder());
279+
280+
if (!wrap->IsAlive())
281+
return args.GetReturnValue().Set(UV_EINVAL);
282+
283+
#if defined(_WIN32)
284+
int rc = 0;
285+
#else
286+
int rc = uv_flush_sync(wrap->stream());
287+
#endif
288+
289+
args.GetReturnValue().Set(rc);
290+
}
291+
292+
276293
int StreamWrap::DoShutdown(ShutdownWrap* req_wrap) {
277294
int err;
278295
err = uv_shutdown(&req_wrap->req_, stream(), AfterShutdown);

src/stream_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class StreamWrap : public HandleWrap, public StreamBase {
7272
int flags = StreamBase::kFlagNone);
7373

7474
private:
75+
static void FlushSync(const v8::FunctionCallbackInfo<v8::Value>& args);
7576
static void SetBlocking(const v8::FunctionCallbackInfo<v8::Value>& args);
7677

7778
// Callbacks for libuv

test/known_issues/known_issues.status

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ prefix known_issues
77
[true] # This section applies to all platforms
88

99
[$system==win32]
10-
test-stdout-buffer-flush-on-exit: SKIP
1110

1211
[$system==linux]
1312

test/known_issues/test-stdout-buffer-flush-on-exit.js test/parallel/test-stdout-buffer-flush-on-exit.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@ if (process.argv[2] === 'child') {
1515
process.exit();
1616
}
1717

18-
[22, 21, 20, 19, 18, 17, 16, 16, 17, 18, 19, 20, 21, 22].forEach((exponent) => {
18+
[22, 16].forEach((exponent) => {
1919
const bigNum = Math.pow(2, exponent);
2020
const longLine = lineSeed.repeat(bigNum);
2121
const cmd = `${process.execPath} ${__filename} child ${exponent} ${bigNum}`;
2222
const stdout = execSync(cmd).toString().trim();
2323

2424
assert.strictEqual(stdout, longLine, `failed with exponent ${exponent}`);
2525
});
26-
27-

0 commit comments

Comments
 (0)