Mercurial > hg
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 |