Issue #23485: select.select() is now retried automatically with the recomputed

timeout when interrupted by a signal, except if the signal handler raises an
exception. This change is part of the PEP 475.

The asyncore and selectors module doesn't catch the InterruptedError exception
anymore when calling select.select(), since this function should not raise
InterruptedError anymore.
This commit is contained in:
Victor Stinner 2015-03-30 21:16:11 +02:00
parent 3f5d48bead
commit f70e1ca0fc
9 changed files with 85 additions and 31 deletions

View File

@ -41,7 +41,10 @@ defined by the module. The specific list of defined symbols is available as
.. data:: EINTR
Interrupted system call
Interrupted system call.
.. seealso::
This error is mapped to the exception :exc:`InterruptedError`.
.. data:: EIO

View File

@ -536,7 +536,12 @@ depending on the system error code.
.. exception:: InterruptedError
Raised when a system call is interrupted by an incoming signal.
Corresponds to :c:data:`errno` ``EINTR``.
Corresponds to :c:data:`errno` :py:data:`~errno.EINTR`.
.. versionchanged:: 3.5
Python now retries system calls when a syscall is interrupted by a
signal, except if the signal handler raises an exception (see :pep:`475`
for the rationale), instead of raising :exc:`InterruptedError`.
.. exception:: IsADirectoryError

View File

@ -145,6 +145,13 @@ The module defines the following:
library, and does not handle file descriptors that don't originate from
WinSock.
.. versionchanged:: 3.5
The function is now retried with a recomputed timeout when interrupted by
a signal, except if the signal handler raises an exception (see
:pep:`475` for the rationale), instead of raising
:exc:`InterruptedError`.
.. attribute:: PIPE_BUF
The minimum number of bytes which can be written without blocking to a pipe

View File

@ -173,9 +173,10 @@ PEP and implementation written by Ben Hoyt with the help of Victor Stinner.
PEP 475: Retry system calls failing with EINTR
----------------------------------------------
:pep:`475` adds support for automatic retry of system calls failing with EINTR:
this means that user code doesn't have to deal with EINTR or InterruptedError
manually, and should make it more robust against asynchronous signal reception.
:pep:`475` adds support for automatic retry of system calls failing with
:py:data:`~errno.EINTR`: this means that user code doesn't have to deal with
EINTR or :exc:`InterruptedError` manually, and should make it more robust
against asynchronous signal reception.
.. seealso::
@ -614,12 +615,13 @@ that may require changes to your code.
Changes in the Python API
-------------------------
* :pep:`475`: the following functions are now retried when interrupted instead
of raising :exc:`InterruptedError` if the signal handler does not raise
an exception:
* :pep:`475`: Examples of functions which are now retried when interrupted
instead of raising :exc:`InterruptedError` if the signal handler does not
raise an exception:
- :func:`os.open`, :func:`open`
- :func:`os.read`, :func:`os.write`
- :func:`select.select`
- :func:`time.sleep`
* Before Python 3.5, a :class:`datetime.time` object was considered to be false

View File

@ -141,10 +141,7 @@ def poll(timeout=0.0, map=None):
time.sleep(timeout)
return
try:
r, w, e = select.select(r, w, e, timeout)
except InterruptedError:
return
for fd in r:
obj = map.get(fd)

View File

@ -310,10 +310,7 @@ class SelectSelector(_BaseSelectorImpl):
def select(self, timeout=None):
timeout = None if timeout is None else max(timeout, 0)
ready = []
try:
r, w, _ = self._select(self._readers, self._writers, [], timeout)
except InterruptedError:
return ready
r = set(r)
w = set(w)
for fd in r | w:

View File

@ -10,6 +10,7 @@ sub-second periodicity (contrarily to signal()).
import io
import os
import select
import signal
import socket
import time
@ -303,12 +304,25 @@ class SignalEINTRTest(EINTRBaseTest):
self.assertGreaterEqual(dt, self.sleep_time)
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
class SelectEINTRTest(EINTRBaseTest):
""" EINTR tests for the select module. """
def test_select(self):
t0 = time.monotonic()
select.select([], [], [], self.sleep_time)
signal.alarm(0)
dt = time.monotonic() - t0
self.assertGreaterEqual(dt, self.sleep_time)
def test_main():
support.run_unittest(
OSEINTRTest,
SocketEINTRTest,
TimeEINTRTest,
SignalEINTRTest)
SignalEINTRTest,
SelectEINTRTest)
if __name__ == "__main__":

View File

@ -13,6 +13,10 @@ Core and Builtins
Library
-------
- Issue #23485: select.select() is now retried automatically with the
recomputed timeout when interrupted by a signal, except if the signal handler
raises an exception. This change is part of the PEP 475.
- Issue #23752: When built from an existing file descriptor, io.FileIO() now
only calls fstat() once. Before fstat() was called twice, which was not
necessary.

View File

@ -193,29 +193,31 @@ select_select(PyObject *self, PyObject *args)
#endif /* SELECT_USES_HEAP */
PyObject *ifdlist, *ofdlist, *efdlist;
PyObject *ret = NULL;
PyObject *tout = Py_None;
PyObject *timeout_obj = Py_None;
fd_set ifdset, ofdset, efdset;
struct timeval tv, *tvp;
int imax, omax, emax, max;
int n;
_PyTime_t timeout, deadline = 0;
/* convert arguments */
if (!PyArg_UnpackTuple(args, "select", 3, 4,
&ifdlist, &ofdlist, &efdlist, &tout))
&ifdlist, &ofdlist, &efdlist, &timeout_obj))
return NULL;
if (tout == Py_None)
tvp = (struct timeval *)0;
if (timeout_obj == Py_None)
tvp = (struct timeval *)NULL;
else {
_PyTime_t ts;
if (_PyTime_FromSecondsObject(&ts, tout, _PyTime_ROUND_CEILING) < 0) {
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
_PyTime_ROUND_CEILING) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be a float or None");
}
return NULL;
}
if (_PyTime_AsTimeval(ts, &tv, _PyTime_ROUND_CEILING) == -1)
if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_CEILING) == -1)
return NULL;
if (tv.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
@ -224,7 +226,6 @@ select_select(PyObject *self, PyObject *args)
tvp = &tv;
}
#ifdef SELECT_USES_HEAP
/* Allocate memory for the lists */
rfd2obj = PyMem_NEW(pylist, FD_SETSIZE + 1);
@ -237,6 +238,7 @@ select_select(PyObject *self, PyObject *args)
return PyErr_NoMemory();
}
#endif /* SELECT_USES_HEAP */
/* Convert sequences to fd_sets, and get maximum fd number
* propagates the Python exception set in seq2set()
*/
@ -249,14 +251,37 @@ select_select(PyObject *self, PyObject *args)
goto finally;
if ((emax=seq2set(efdlist, &efdset, efd2obj)) < 0)
goto finally;
max = imax;
if (omax > max) max = omax;
if (emax > max) max = emax;
if (tvp)
deadline = _PyTime_GetMonotonicClock() + timeout;
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
n = select(max, &ifdset, &ofdset, &efdset, tvp);
Py_END_ALLOW_THREADS
if (errno != EINTR)
break;
/* select() was interrupted by a signal */
if (PyErr_CheckSignals())
goto finally;
if (tvp) {
timeout = deadline - _PyTime_GetMonotonicClock();
if (timeout < 0) {
n = 0;
break;
}
_PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
}
} while (1);
#ifdef MS_WINDOWS
if (n == SOCKET_ERROR) {
PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError());