Skip to content

Commit 98de4ab

Browse files
committed
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 nodejs#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 c3cec1e, which was included in v6.0.0. Full details of the Libuv issue that induced this are at nodejs#6456 (comment) Refs: nodejs#1771 Refs: nodejs#6456 Refs: nodejs#6773 Refs: nodejs#6816 PR-URL: nodejs#6895 Reviewed-By: Rod Vagg <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent f81f0c3 commit 98de4ab

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

doc/api/console.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,15 @@ duplicate the browser's functionality exactly.
5353

5454
## Asynchronous vs Synchronous Consoles
5555

56-
The console functions are asynchronous unless the destination is a file.
56+
The console functions are usually asynchronous unless the destination is a file.
5757
Disks are fast and operating systems normally employ write-back caching;
5858
it should be a very rare occurrence indeed that a write blocks, but it
5959
is possible.
6060

61+
Additionally, console functions are blocking when outputting to TTYs
62+
(terminals) on OS X as a workaround for the OS's very small, 1kb buffer size.
63+
This is to prevent interleaving between `stdout` and `stderr`.
64+
6165
## Class: Console
6266

6367
<!--type=class-->

doc/api/process.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,8 @@ if (someConditionNotMet()) {
833833
```
834834

835835
The reason this is problematic is because writes to `process.stdout` in Node.js
836-
are *non-blocking* and may occur over multiple ticks of the Node.js event loop.
836+
are usually *non-blocking* and may occur over multiple ticks of the Node.js
837+
event loop.
837838
Calling `process.exit()`, however, forces the process to exit *before* those
838839
additional writes to `stdout` can be performed.
839840

@@ -1367,6 +1368,10 @@ event and that writes can block when output is redirected to a file (although
13671368
disks are fast and operating systems normally employ write-back caching so it
13681369
should be a very rare occurrence indeed.)
13691370

1371+
Additionally, `process.stderr` and `process.stdout` are blocking when outputting
1372+
to TTYs (terminals) on OS X as a workaround for the OS's very small, 1kb
1373+
buffer size. This is to prevent interleaving between `stdout` and `stderr`.
1374+
13701375
## process.stdin
13711376

13721377
A `Readable Stream` for stdin (on fd `0`).
@@ -1417,6 +1422,10 @@ event and that writes can block when output is redirected to a file (although
14171422
disks are fast and operating systems normally employ write-back caching so it
14181423
should be a very rare occurrence indeed.)
14191424

1425+
Additionally, `process.stderr` and `process.stdout` are blocking when outputting
1426+
to TTYs (terminals) on OS X as a workaround for the OS's very small, 1kb
1427+
buffer size. This is to prevent interleaving between `stdout` and `stderr`.
1428+
14201429
To check if Node.js is being run in a TTY context, read the `isTTY` property
14211430
on `process.stderr`, `process.stdout`, or `process.stdin`:
14221431

lib/tty.js

+7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ function WriteStream(fd) {
4848
writable: true
4949
});
5050

51+
// Prevents interleaved stdout/stderr output in OS X terminals.
52+
// As noted in the following reference, local TTYs tend to be quite fast and
53+
// this behaviour has become expected due historical functionality on OS X,
54+
// even though it was originally intended to change in v1.0.2 (Libuv 1.2.1).
55+
// Ref: https://github.com/nodejs/node/pull/1771#issuecomment-119351671
56+
if (process.platform === 'darwin') this._handle.setBlocking(true);
57+
5158
var winSize = [];
5259
var err = this._handle.getWindowSize(winSize);
5360
if (!err) {

0 commit comments

Comments
 (0)