From 9eb7dc898e7163c660dc9331e68fb4349c7ffa17 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Fri, 20 May 2016 10:20:08 -0400 Subject: [PATCH] tty: use blocking mode on OS X OS X has a tiny 1kb hard-coded buffer size for stdout / stderr to TTYs (terminals). Output larger than that causes chunking, which ends up having some (very small but existent) delay past the first chunk. That causes two problems: 1. When output is written to stdout and stderr at similar times, the two can become mixed together (interleaved). This is especially problematic when using control characters, such as \r. With interleaving, chunked output will often have lines or characters erased unintentionally, or in the wrong spots, leading to broken output. CLI apps often extensively use such characters for things such as progress bars. 2. Output can be lost if the process is exited before chunked writes are finished flushing. This usually happens in applications that use `process.exit()`, which isn't infrequent. See https://github.com/nodejs/node/issues/6980 for more info. This became an issue as result of the Libuv 1.9.0 upgrade. A fix to an unrelated issue broke a hack previously required for the OS X implementation. This resulted in an unexpected behavior change in node. The 1.9.0 upgrade was done in c3cec1eefc9f3b55a3fb7bd623b3d921f493870d, which was included in v6.0.0. Full details of the Libuv issue that induced this are at https://github.com/nodejs/node/issues/6456#issuecomment-219974514 Refs: https://github.com/nodejs/node/pull/1771 Refs: https://github.com/nodejs/node/issues/6456 Refs: https://github.com/nodejs/node/pull/6773 Refs: https://github.com/nodejs/node/pull/6816 PR-URL: https://github.com/nodejs/node/pull/6895 Reviewed-By: Rod Vagg Reviewed-By: Anna Henningsen --- doc/api/console.md | 6 +++++- doc/api/process.md | 10 +++++++++- lib/tty.js | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/api/console.md b/doc/api/console.md index 67d82a3ed55eab..ead2c3607011a5 100644 --- a/doc/api/console.md +++ b/doc/api/console.md @@ -53,11 +53,15 @@ duplicate the browser's functionality exactly. ## Asynchronous vs Synchronous Consoles -The console functions are asynchronous unless the destination is a file. +The console functions are usually asynchronous unless the destination is a file. Disks are fast and operating systems normally employ write-back caching; it should be a very rare occurrence indeed that a write blocks, but it is possible. +Additionally, console functions are blocking when outputting to TTYs +(terminals) on OS X as a workaround for the OS's very small, 1kb buffer size. +This is to prevent interleaving between `stdout` and `stderr`. + ## Class: Console diff --git a/doc/api/process.md b/doc/api/process.md index c7f2567ccf5aff..97cabdf8ad18b0 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -953,6 +953,10 @@ event and that writes can block when output is redirected to a file (although disks are fast and operating systems normally employ write-back caching so it should be a very rare occurrence indeed.) +Additionally, `process.stderr` and `process.stdout` are blocking when outputting +to TTYs (terminals) on OS X as a workaround for the OS's very small, 1kb +buffer size. This is to prevent interleaving between `stdout` and `stderr`. + ## process.stdin A `Readable Stream` for stdin (on fd `0`). @@ -1003,6 +1007,10 @@ event and that writes can block when output is redirected to a file (although disks are fast and operating systems normally employ write-back caching so it should be a very rare occurrence indeed.) +Additionally, `process.stderr` and `process.stdout` are blocking when outputting +to TTYs (terminals) on OS X as a workaround for the OS's very small, 1kb +buffer size. This is to prevent interleaving between `stdout` and `stderr`. + To check if Node.js is being run in a TTY context, read the `isTTY` property on `process.stderr`, `process.stdout`, or `process.stdin`: @@ -1098,4 +1106,4 @@ Will print something like: [Signal Events]: #process_signal_events [Stream compatibility]: stream.html#stream_compatibility_with_older_node_js_versions [the tty docs]: tty.html#tty_tty -[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify \ No newline at end of file +[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify diff --git a/lib/tty.js b/lib/tty.js index f3f84ca5a6e9f7..54a7b756ec9544 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -59,6 +59,13 @@ function WriteStream(fd) { writable: true }); + // Prevents interleaved stdout/stderr output in OS X terminals. + // As noted in the following reference, local TTYs tend to be quite fast and + // this behaviour has become expected due historical functionality on OS X, + // even though it was originally intended to change in v1.0.2 (Libuv 1.2.1). + // Ref: https://github.com/nodejs/node/pull/1771#issuecomment-119351671 + if (process.platform === 'darwin') this._handle.setBlocking(true); + var winSize = []; var err = this._handle.getWindowSize(winSize); if (!err) {