comparison mercurial/progress.py @ 32049:ed42e00a5c4e

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
author Yuya Nishihara <yuya@tcha.org>
date Thu, 13 Apr 2017 22:31:17 +0900
parents c3ef33fd0058
children 391da1416038
comparison
equal deleted inserted replaced
32048:c3ef33fd0058 32049:ed42e00a5c4e
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import 8 from __future__ import absolute_import
9 9
10 import errno
10 import threading 11 import threading
11 import time 12 import time
12 13
13 from .i18n import _ 14 from .i18n import _
14 from . import encoding 15 from . import encoding
57 weeks += 1 58 weeks += 1
58 years = weeks // 52 59 years = weeks // 52
59 weeks -= years * 52 60 weeks -= years * 52
60 # i18n: format X years and YY weeks as "XyYYw" 61 # i18n: format X years and YY weeks as "XyYYw"
61 return _("%dy%02dw") % (years, weeks) 62 return _("%dy%02dw") % (years, weeks)
63
64 # file_write() and file_flush() of Python 2 do not restart on EINTR if
65 # the file is attached to a "slow" device (e.g. a terminal) and raise
66 # IOError. We cannot know how many bytes would be written by file_write(),
67 # but a progress text is known to be short enough to be written by a
68 # single write() syscall, so we can just retry file_write() with the whole
69 # text. (issue5532)
70 #
71 # This should be a short-term workaround. We'll need to fix every occurrence
72 # of write() to a terminal or pipe.
73 def _eintrretry(func, *args):
74 while True:
75 try:
76 return func(*args)
77 except IOError as err:
78 if err.errno == errno.EINTR:
79 continue
80 raise
62 81
63 class progbar(object): 82 class progbar(object):
64 def __init__(self, ui): 83 def __init__(self, ui):
65 self.ui = ui 84 self.ui = ui
66 self._refreshlock = threading.Lock() 85 self._refreshlock = threading.Lock()
177 else: 196 else:
178 self._writeerr('\n') 197 self._writeerr('\n')
179 self._flusherr() 198 self._flusherr()
180 199
181 def _flusherr(self): 200 def _flusherr(self):
182 self.ui.ferr.flush() 201 _eintrretry(self.ui.ferr.flush)
183 202
184 def _writeerr(self, msg): 203 def _writeerr(self, msg):
185 self.ui.ferr.write(msg) 204 _eintrretry(self.ui.ferr.write, msg)
186 205
187 def width(self): 206 def width(self):
188 tw = self.ui.termwidth() 207 tw = self.ui.termwidth()
189 return min(int(self.ui.config('progress', 'width', default=tw)), tw) 208 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
190 209