Patchwork [3,of,6,v2] util: always force line buffered stdout when stdout is a tty (BC)

login
register
mail settings
Submitter Simon Farnsworth
Date Feb. 2, 2017, 7:18 p.m.
Message ID <722c309600ed9596a026.1486063137@devvm022.lla2.facebook.com>
Download mbox | patch
Permalink /patch/18302/
State Accepted
Headers show

Comments

Simon Farnsworth - Feb. 2, 2017, 7:18 p.m.
# HG changeset patch
# User Simon Farnsworth <simonfar@fb.com>
# Date 1486063056 28800
#      Thu Feb 02 11:17:36 2017 -0800
# Node ID 722c309600ed9596a02674b04cb2caa9a65e8918
# Parent  12d0ac224bb34691d44a2cead5b9795a6cfc2490
util: always force line buffered stdout when stdout is a tty (BC)

pager replaced stdout with a line buffered version to work around glibc
deciding on a buffering strategy on the first write to stdout. This is going
to make my next patch hard, as replacing stdout will make tracking time
spent blocked on it more challenging.

Move the line buffering requirement to util.py, and remove it from pager.
This means that the abuse of ui.formatted=True and pager set to cat or equivalent
no longer results in a line-buffered output to a pipe, hence (BC), although
I don't expect anyone to be affected
Yuya Nishihara - Feb. 3, 2017, 1:54 p.m.
On Thu, 2 Feb 2017 11:18:57 -0800, Simon Farnsworth wrote:
> # HG changeset patch
> # User Simon Farnsworth <simonfar@fb.com>
> # Date 1486063056 28800
> #      Thu Feb 02 11:17:36 2017 -0800
> # Node ID 722c309600ed9596a02674b04cb2caa9a65e8918
> # Parent  12d0ac224bb34691d44a2cead5b9795a6cfc2490
> util: always force line buffered stdout when stdout is a tty (BC)
> 
> pager replaced stdout with a line buffered version to work around glibc
> deciding on a buffering strategy on the first write to stdout. This is going
> to make my next patch hard, as replacing stdout will make tracking time
> spent blocked on it more challenging.
> 
> Move the line buffering requirement to util.py, and remove it from pager.
> This means that the abuse of ui.formatted=True and pager set to cat or equivalent
> no longer results in a line-buffered output to a pipe, hence (BC), although
> I don't expect anyone to be affected

Sounds okay. A few nits follow.

> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -69,6 +69,12 @@
>  else:
>      from . import posix as platform
>  
> +# glibc determines buffering on first write to stdout - if we replace a TTY
> +# destined stdout with a pipe destined stdout (e.g. pager), we want line
> +# buffering
> +if stdout.isatty():

stdout might be replaced with a file-like object. Can we use util.isatty() ?

> +    stdout = os.fdopen(stdout.fileno(), 'wb', 1)

Perhaps this should be done before stdout is wrapped by winstdout().

Patch

diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -87,14 +87,10 @@ 
                              close_fds=util.closefds, stdin=subprocess.PIPE,
                              stdout=util.stdout, stderr=util.stderr)
 
-    # back up original file objects and descriptors
-    olduifout = ui.fout
-    oldstdout = util.stdout
+    # back up original file descriptors
     stdoutfd = os.dup(util.stdout.fileno())
     stderrfd = os.dup(util.stderr.fileno())
 
-    # create new line-buffered stdout so that output can show up immediately
-    ui.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(), 'wb', 1)
     os.dup2(pager.stdin.fileno(), util.stdout.fileno())
     if ui._isatty(util.stderr):
         os.dup2(pager.stdin.fileno(), util.stderr.fileno())
@@ -103,15 +99,10 @@ 
     def killpager():
         if util.safehasattr(signal, "SIGINT"):
             signal.signal(signal.SIGINT, signal.SIG_IGN)
-        pager.stdin.close()
-        ui.fout = olduifout
-        util.stdout = oldstdout
-        # close new stdout while it's associated with pager; otherwise stdout
-        # fd would be closed when newstdout is deleted
-        newstdout.close()
-        # restore original fds: stdout is open again
+        # restore original fds, closing pagin.stdin copies in the process
         os.dup2(stdoutfd, util.stdout.fileno())
         os.dup2(stderrfd, util.stderr.fileno())
+        pager.stdin.close()
         pager.wait()
 
 def uisetup(ui):
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -69,6 +69,12 @@ 
 else:
     from . import posix as platform
 
+# glibc determines buffering on first write to stdout - if we replace a TTY
+# destined stdout with a pipe destined stdout (e.g. pager), we want line
+# buffering
+if stdout.isatty():
+    stdout = os.fdopen(stdout.fileno(), 'wb', 1)
+
 _ = i18n._
 
 bindunixsocket = platform.bindunixsocket