Patchwork [2,of,3] posix: implement readpipe using non-blocking I/O (issue4336)

login
register
mail settings
Submitter Gregory Szorc
Date Aug. 16, 2014, 5:37 p.m.
Message ID <03583229bdf7dc6ee0c0.1408210641@vm-ubuntu-main.gateway.sonic.net>
Download mbox | patch
Permalink /patch/5454/
State Accepted
Headers show

Comments

Gregory Szorc - Aug. 16, 2014, 5:37 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1408210139 25200
#      Sat Aug 16 10:28:59 2014 -0700
# Node ID 03583229bdf7dc6ee0c0edf082b092edae714307
# Parent  90f91db78e94294e5940d0191f331323b97b3a96
posix: implement readpipe using non-blocking I/O (issue4336)

On Linux, fstat().st_size of a pipe always returns 0, even if the
pipe has data available for reading. This meant that reading from
and subsequently printing the stderr pipe content after wireproto
commands over SSH meant that available data wasn't being printed.

We now implement pipe reading on POSIX by doing a non-blocking
read for all available data.

Patch

diff --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -7,8 +7,9 @@ 
 
 from i18n import _
 import encoding
 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
+import fcntl
 
 posixfile = open
 normpath = os.path.normpath
 samestat = os.path.samestat
@@ -431,9 +432,9 @@  def gethgcmd():
     return sys.argv[:1]
 
 def termwidth():
     try:
-        import termios, array, fcntl
+        import termios, array
         for dev in (sys.stderr, sys.stdout, sys.stdin):
             try:
                 try:
                     fd = dev.fileno()
@@ -569,14 +570,25 @@  def statisexec(st):
     return st and (st.st_mode & 0100 != 0)
 
 def readpipe(pipe):
     """Read all available data from a pipe."""
-    chunks = []
-    while True:
-        size = os.fstat(pipe.fileno()).st_size
-        if not size:
-            break
+    # We can't fstat() a pipe because Linux will always report 0.
+    # So, we set the pipe to non-blocking mode and read everything
+    # that's available.
+    flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
+    flags |= os.O_NONBLOCK
+    oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
 
-        s = pipe.read(size)
-        if not s:
-            break
-        chunks.append(s)
+    try:
+        chunks = []
+        while True:
+            try:
+                s = pipe.read()
+                if not s:
+                    break
+                chunks.append(s)
+            except IOError:
+                break
+
+        return ''.join(chunks)
+    finally:
+        fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)