Should I handle EINTR "system call interrupted" errors when reading a pipe in Python, and if so, how do I test such code?
In the traceback below, self._dataq
is a multiprocessing.Queue
(technically, I'm using the billiard
library, but I think they're basically the same code). A Python subprocess occasionally writes to the other end of the queue. I think what happened is that a system call was reading the pipe that feeds the queue, and a signal arrived -- possibly a SIGINT from a second Ctrl+C event (the first SIGINT occurred where you see the user's ^C
on the second line of the logging output, and my signal handler caught that SIGINT, as you can see in the WARNING message in the log).
[INFO 2014-03-05 14:16:06,000] Doing some work, la-dee-da
^C[WARNING 2014-03-05 14:16:07,344] Commencing shutdown. (Signal SIGINT, process 2469.). Press Ctrl+C again to exit immediately.
[DEBUG 2014-03-05 14:16:07,347] Terminating subprocess
Traceback (most recent call last):
[... a bunch of stuff omitted]
File "mycode.py", line 97, in __next__
result = self._dataq.get(timeout=0.1)
File "/usr/local/lib/python2.7/site-packages/billiard/queues.py", line 103, in get
if timeout < 0 or not self._poll(timeout):
IOError: [Errno 4] Interrupted system call
The statement result = self._dataq.get(timeout=0.1)
in the traceback above is in the middle of a loop that looks like the following. The main purpose of the loop is so that I can give up trying to read from self._dataq
when self.timedout()
starts returning True
.
import queue
while True:
try:
result = self._dataq.get(timeout=0.1)
except queue.Empty:
if self.timedout():
self.close()
raise MyTimedoutError()
else:
break
If my theory about why the IOError
occurred is correct, then the try
... except
block above should catch and ignore IOError
s when they're caused by interrupted system calls. If it is a signal that caused the EINTR error, than the mere act of coming back into Python to run the except IOError:
statement would allow Python-level signal handlers to run.
Is that correct? If so, is it possible to test this change in my code? It's not obvious to me how I would write a unit test that wouldn't contain a severe race condition.
Python 3.5 solves this problem by putting the responsibility for dealing with EINTR
on the Python runtime instead of on application code. See PEP 475 and the Python 3.5 Changelog .
I'm going to call this a bug in Python. I cannot find any documentation that multiprocessing.Queue
is allowed to raise IOError
on EINTR
(though it would make sense to raise on other I/O issues, which is why you should check the errno attribute of the raised exception before ignoring it; see errno
), and it doesn't directly map to any low-level C function which would provide such behavior. There was some discussion of handling all EINTR
s at the C level, but that didn't make it into 3.4 (and I doubt it will make it into 2.x at all), so this is probably still fair game to report.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.