Issue #23485: select.epoll.poll() is now retried when interrupted by a signal
This commit is contained in:
parent
3c7d6e0693
commit
41eba224de
|
@ -329,6 +329,12 @@ Edge and Level Trigger Polling (epoll) Objects
|
||||||
|
|
||||||
Wait for events. timeout in seconds (float)
|
Wait for events. timeout in seconds (float)
|
||||||
|
|
||||||
|
.. 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`.
|
||||||
|
|
||||||
|
|
||||||
.. _poll-objects:
|
.. _poll-objects:
|
||||||
|
|
||||||
|
|
|
@ -621,7 +621,7 @@ Changes in the Python API
|
||||||
|
|
||||||
- :func:`os.open`, :func:`open`
|
- :func:`os.open`, :func:`open`
|
||||||
- :func:`os.read`, :func:`os.write`
|
- :func:`os.read`, :func:`os.write`
|
||||||
- :func:`select.select`, :func:`select.poll.poll`
|
- :func:`select.select`, :func:`select.poll.poll`, :func:`select.epoll.poll`
|
||||||
- :func:`time.sleep`
|
- :func:`time.sleep`
|
||||||
|
|
||||||
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
|
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
|
||||||
|
|
|
@ -423,11 +423,9 @@ if hasattr(select, 'epoll'):
|
||||||
# FD is registered.
|
# FD is registered.
|
||||||
max_ev = max(len(self._fd_to_key), 1)
|
max_ev = max(len(self._fd_to_key), 1)
|
||||||
|
|
||||||
|
fd_event_list = self._epoll.poll(timeout, max_ev)
|
||||||
|
|
||||||
ready = []
|
ready = []
|
||||||
try:
|
|
||||||
fd_event_list = self._epoll.poll(timeout, max_ev)
|
|
||||||
except InterruptedError:
|
|
||||||
return ready
|
|
||||||
for fd, event in fd_event_list:
|
for fd, event in fd_event_list:
|
||||||
events = 0
|
events = 0
|
||||||
if event & ~select.EPOLLIN:
|
if event & ~select.EPOLLIN:
|
||||||
|
|
|
@ -329,6 +329,17 @@ class SelectEINTRTest(EINTRBaseTest):
|
||||||
dt = time.monotonic() - t0
|
dt = time.monotonic() - t0
|
||||||
self.assertGreaterEqual(dt, self.sleep_time)
|
self.assertGreaterEqual(dt, self.sleep_time)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll')
|
||||||
|
def test_epoll(self):
|
||||||
|
poller = select.epoll()
|
||||||
|
self.addCleanup(poller.close)
|
||||||
|
|
||||||
|
t0 = time.monotonic()
|
||||||
|
poller.poll(self.sleep_time)
|
||||||
|
self.stop_alarm()
|
||||||
|
dt = time.monotonic() - t0
|
||||||
|
self.assertGreaterEqual(dt, self.sleep_time)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(
|
support.run_unittest(
|
||||||
|
|
|
@ -535,7 +535,7 @@ poll_poll(pollObject *self, PyObject *args)
|
||||||
if (timeout_obj == NULL || timeout_obj == Py_None) {
|
if (timeout_obj == NULL || timeout_obj == Py_None) {
|
||||||
timeout = -1;
|
timeout = -1;
|
||||||
ms = -1;
|
ms = -1;
|
||||||
deadline = 0;
|
deadline = 0; /* initialize to prevent gcc warning */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
||||||
|
@ -1465,34 +1465,46 @@ fd is the target file descriptor of the operation.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
|
pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
double dtimeout = -1.;
|
static char *kwlist[] = {"timeout", "maxevents", NULL};
|
||||||
int timeout;
|
PyObject *timeout_obj = NULL;
|
||||||
int maxevents = -1;
|
int maxevents = -1;
|
||||||
int nfds, i;
|
int nfds, i;
|
||||||
PyObject *elist = NULL, *etuple = NULL;
|
PyObject *elist = NULL, *etuple = NULL;
|
||||||
struct epoll_event *evs = NULL;
|
struct epoll_event *evs = NULL;
|
||||||
static char *kwlist[] = {"timeout", "maxevents", NULL};
|
_PyTime_t timeout, ms, deadline;
|
||||||
|
|
||||||
if (self->epfd < 0)
|
if (self->epfd < 0)
|
||||||
return pyepoll_err_closed();
|
return pyepoll_err_closed();
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|di:poll", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:poll", kwlist,
|
||||||
&dtimeout, &maxevents)) {
|
&timeout_obj, &maxevents)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dtimeout < 0) {
|
if (timeout_obj == NULL || timeout_obj == Py_None) {
|
||||||
timeout = -1;
|
timeout = -1;
|
||||||
}
|
ms = -1;
|
||||||
else if (dtimeout * 1000.0 > INT_MAX) {
|
deadline = 0; /* initialize to prevent gcc warning */
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
|
||||||
"timeout is too large");
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* epoll_wait() has a resolution of 1 millisecond, round away from zero
|
/* epoll_wait() has a resolution of 1 millisecond, round towards
|
||||||
to wait *at least* dtimeout seconds. */
|
infinity to wait at least timeout seconds. */
|
||||||
timeout = (int)ceil(dtimeout * 1000.0);
|
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
||||||
|
_PyTime_ROUND_CEILING) < 0) {
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"timeout must be an integer or None");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
||||||
|
if (ms < INT_MIN || ms > INT_MAX) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
deadline = _PyTime_GetMonotonicClock() + timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxevents == -1) {
|
if (maxevents == -1) {
|
||||||
|
@ -1511,9 +1523,30 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
do {
|
||||||
nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
|
Py_BEGIN_ALLOW_THREADS
|
||||||
Py_END_ALLOW_THREADS
|
errno = 0;
|
||||||
|
nfds = epoll_wait(self->epfd, evs, maxevents, (int)ms);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if (errno != EINTR)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* poll() was interrupted by a signal */
|
||||||
|
if (PyErr_CheckSignals())
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (timeout >= 0) {
|
||||||
|
timeout = deadline - _PyTime_GetMonotonicClock();
|
||||||
|
if (timeout < 0) {
|
||||||
|
nfds = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
||||||
|
/* retry epoll_wait() with the recomputed timeout */
|
||||||
|
}
|
||||||
|
} while(1);
|
||||||
|
|
||||||
if (nfds < 0) {
|
if (nfds < 0) {
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
Loading…
Reference in New Issue