windows: fix incorrect detection of broken pipe when writing to pager stable
authorSune Foldager <cryo@cyanite.org>
Wed, 04 Jul 2018 14:19:13 +0200
branchstable
changeset 37882 3a0f322af192
parent 37881 173cfdde0c86
child 37883 443029011990
child 38602 af8d8513d7de
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.
mercurial/win32.py
mercurial/windows.py
--- a/mercurial/win32.py	Tue Jul 03 12:10:22 2018 -0400
+++ b/mercurial/win32.py	Wed Jul 04 14:19:13 2018 +0200
@@ -44,6 +44,7 @@
 _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 @@
 
     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'''
--- a/mercurial/windows.py	Tue Jul 03 12:10:22 2018 -0400
+++ b/mercurial/windows.py	Wed Jul 04 14:19:13 2018 +0200
@@ -172,7 +172,7 @@
                 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 @@
         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')