# HG changeset patch # User Yuya Nishihara # Date 1492090277 -32400 # Node ID ed42e00a5c4ec7a3bebaec699ef934429cdc2ddd # Parent c3ef33fd005825ebf10569e9a4d6e0e78fd4567b 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 diff -r c3ef33fd0058 -r ed42e00a5c4e mercurial/progress.py --- 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()