progress: retry ferr.flush() and .write() on EINTR (
issue5532)
See the inline comment how this could mitigate the issue.
I couldn't reproduce the exact problem on my Linux machine, but there are
at least two people who got EINTR in progress.py, and it seems file_write()
of Python 2 is fundamentally broken [1]. Let's make something in on 4.2.
[1]: https://hg.python.org/cpython/file/v2.7.13/Objects/fileobject.c#l1850
--- a/mercurial/progress.py Thu Apr 13 22:27:25 2017 +0900
+++ b/mercurial/progress.py Thu Apr 13 22:31:17 2017 +0900
@@ -7,6 +7,7 @@
from __future__ import absolute_import
+import errno
import threading
import time
@@ -60,6 +61,24 @@
# i18n: format X years and YY weeks as "XyYYw"
return _("%dy%02dw") % (years, weeks)
+# file_write() and file_flush() of Python 2 do not restart on EINTR if
+# the file is attached to a "slow" device (e.g. a terminal) and raise
+# IOError. We cannot know how many bytes would be written by file_write(),
+# but a progress text is known to be short enough to be written by a
+# single write() syscall, so we can just retry file_write() with the whole
+# text. (issue5532)
+#
+# This should be a short-term workaround. We'll need to fix every occurrence
+# of write() to a terminal or pipe.
+def _eintrretry(func, *args):
+ while True:
+ try:
+ return func(*args)
+ except IOError as err:
+ if err.errno == errno.EINTR:
+ continue
+ raise
+
class progbar(object):
def __init__(self, ui):
self.ui = ui
@@ -179,10 +198,10 @@
self._flusherr()
def _flusherr(self):
- self.ui.ferr.flush()
+ _eintrretry(self.ui.ferr.flush)
def _writeerr(self, msg):
- self.ui.ferr.write(msg)
+ _eintrretry(self.ui.ferr.write, msg)
def width(self):
tw = self.ui.termwidth()