[PATCH STABLE] windows: fix incorrect detection of broken pipe when writing to pager

Sune Foldager sune.foldager at me.com
Wed Jul 4 12:20:26 UTC 2018


# HG changeset patch
# User Sune Foldager <cryo at cyanite.org>
# Date 1530706753 -7200
#      Wed Jul 04 14:19:13 2018 +0200
# Branch stable
# Node ID 3a0f322af1926e52322d2cff90a71529f359f2d9
# Parent  173cfdde0c8611fd86d26c0467b42aabf03bda0f
windows: fix incorrect detection of broken pipe when writing to pager

Paging e.g. hg incoming on Windows and quitting the pager before the
output is consumed will print 'abort: Invalid argument'. This is
because the windows error 0xE8 (ERROR_NO_DATA) is mapped to EINVAL
even though it is documented as 'The pipe is being closed'.

Note that this fix assumes that Windows' last error code is still
valid in the exception handler. It works correctly in all my tests.

A simpler fix would be to just map EINVAL to EPIPE, like was done is
flush previously, but that would be less precise.

This error was not observed previously, when pager was an extension.

diff --git a/mercurial/win32.py b/mercurial/win32.py
--- a/mercurial/win32.py
+++ b/mercurial/win32.py
@@ -44,6 +44,7 @@ from . import (
 _ERROR_INVALID_PARAMETER = 87
 _ERROR_BROKEN_PIPE = 109
 _ERROR_INSUFFICIENT_BUFFER = 122
+_ERROR_NO_DATA = 232
 
 # WPARAM is defined as UINT_PTR (unsigned type)
 # LPARAM is defined as LONG_PTR (signed type)
@@ -406,6 +407,12 @@ def peekpipe(pipe):
 
     return avail.value
 
+def lasterrorwaspipeerror(err):
+    if err.errno != errno.EINVAL:
+        return False
+    err = _kernel32.GetLastError()
+    return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
+
 def testpid(pid):
     '''return True if pid is still running or unable to
     determine, False otherwise'''
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -172,7 +172,7 @@ class winstdout(object):
                 self.fp.write(s[start:end])
                 start = end
         except IOError as inst:
-            if inst.errno != 0:
+            if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
                 raise
             self.close()
             raise IOError(errno.EPIPE, 'Broken pipe')
@@ -181,7 +181,7 @@ class winstdout(object):
         try:
             return self.fp.flush()
         except IOError as inst:
-            if inst.errno != errno.EINVAL:
+            if not win32.lasterrorwaspipeerror(inst):
                 raise
             raise IOError(errno.EPIPE, 'Broken pipe')
 


More information about the Mercurial-devel mailing list