Kaydet (Commit) 45ca48b0 authored tarafından Victor Stinner's avatar Victor Stinner

Issue #23485: select.devpoll.poll() is now retried when interrupted by a signal

üst 4448c084
...@@ -249,6 +249,12 @@ object. ...@@ -249,6 +249,12 @@ object.
returning. If *timeout* is omitted, -1, or :const:`None`, the call will returning. If *timeout* is omitted, -1, or :const:`None`, the call will
block until there is an event for this poll object. block until there is an event for this poll object.
.. 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`.
.. _epoll-objects: .. _epoll-objects:
......
...@@ -619,10 +619,10 @@ Changes in the Python API ...@@ -619,10 +619,10 @@ Changes in the Python API
instead of raising :exc:`InterruptedError` if the signal handler does not instead of raising :exc:`InterruptedError` if the signal handler does not
raise an exception: raise an exception:
- :func:`os.open`, :func:`open` - :func:`open`, :func:`os.open`, :func:`io.open`
- :func:`os.read`, :func:`os.write` - :func:`os.read`, :func:`os.write`
- :func:`select.select`, :func:`select.poll.poll`, :func:`select.epoll.poll`, - :func:`select.select`, :func:`select.poll.poll`, :func:`select.epoll.poll`,
:func:`select.kqueue.control` :func:`select.kqueue.control`, :func:`select.devpoll.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
......
...@@ -479,11 +479,10 @@ if hasattr(select, 'devpoll'): ...@@ -479,11 +479,10 @@ if hasattr(select, 'devpoll'):
# devpoll() has a resolution of 1 millisecond, round away from # devpoll() has a resolution of 1 millisecond, round away from
# zero to wait *at least* timeout seconds. # zero to wait *at least* timeout seconds.
timeout = math.ceil(timeout * 1e3) timeout = math.ceil(timeout * 1e3)
fd_event_list = self._devpoll.poll(timeout)
ready = [] ready = []
try:
fd_event_list = self._devpoll.poll(timeout)
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.POLLIN: if event & ~select.POLLIN:
......
...@@ -351,6 +351,17 @@ class SelectEINTRTest(EINTRBaseTest): ...@@ -351,6 +351,17 @@ class SelectEINTRTest(EINTRBaseTest):
self.stop_alarm() self.stop_alarm()
self.assertGreaterEqual(dt, self.sleep_time) self.assertGreaterEqual(dt, self.sleep_time)
@unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll')
def test_devpoll(self):
poller = select.devpoll()
self.addCleanup(poller.close)
t0 = time.monotonic()
poller.poll(self.sleep_time * 1e3)
dt = time.monotonic() - t0
self.stop_alarm()
self.assertGreaterEqual(dt, self.sleep_time)
def test_main(): def test_main():
support.run_unittest( support.run_unittest(
......
...@@ -876,40 +876,38 @@ static PyObject * ...@@ -876,40 +876,38 @@ static PyObject *
devpoll_poll(devpollObject *self, PyObject *args) devpoll_poll(devpollObject *self, PyObject *args)
{ {
struct dvpoll dvp; struct dvpoll dvp;
PyObject *result_list = NULL, *tout = NULL; PyObject *result_list = NULL, *timeout_obj = NULL;
int poll_result, i; int poll_result, i;
long timeout;
PyObject *value, *num1, *num2; PyObject *value, *num1, *num2;
_PyTime_t timeout, ms, deadline = 0;
if (self->fd_devpoll < 0) if (self->fd_devpoll < 0)
return devpoll_err_closed(); return devpoll_err_closed();
if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) { if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL; return NULL;
} }
/* Check values for timeout */ /* Check values for timeout */
if (tout == NULL || tout == Py_None) if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1; timeout = -1;
else if (!PyNumber_Check(tout)) { ms = -1;
PyErr_SetString(PyExc_TypeError,
"timeout must be an integer or None");
return NULL;
} }
else { else {
tout = PyNumber_Long(tout); if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
if (!tout) _PyTime_ROUND_CEILING) < 0) {
return NULL; if (PyErr_ExceptionMatches(PyExc_TypeError)) {
timeout = PyLong_AsLong(tout); PyErr_SetString(PyExc_TypeError,
Py_DECREF(tout); "timeout must be an integer or None");
if (timeout == -1 && PyErr_Occurred()) }
return NULL; return NULL;
} }
if ((timeout < -1) || (timeout > INT_MAX)) { ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
PyErr_SetString(PyExc_OverflowError, if (ms < -1 || ms > INT_MAX) {
"timeout is out of range"); PyErr_SetString(PyExc_OverflowError, "timeout is too large");
return NULL; return NULL;
}
} }
if (devpoll_flush(self)) if (devpoll_flush(self))
...@@ -917,12 +915,36 @@ devpoll_poll(devpollObject *self, PyObject *args) ...@@ -917,12 +915,36 @@ devpoll_poll(devpollObject *self, PyObject *args)
dvp.dp_fds = self->fds; dvp.dp_fds = self->fds;
dvp.dp_nfds = self->max_n_fds; dvp.dp_nfds = self->max_n_fds;
dvp.dp_timeout = timeout; dvp.dp_timeout = (int)ms;
if (timeout >= 0)
deadline = _PyTime_GetMonotonicClock() + timeout;
do {
/* call devpoll() */
Py_BEGIN_ALLOW_THREADS
errno = 0;
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
Py_END_ALLOW_THREADS
if (errno != EINTR)
break;
/* call devpoll() */ /* devpoll() was interrupted by a signal */
Py_BEGIN_ALLOW_THREADS if (PyErr_CheckSignals())
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp); return NULL;
Py_END_ALLOW_THREADS
if (timeout >= 0) {
timeout = deadline - _PyTime_GetMonotonicClock();
if (timeout < 0) {
poll_result = 0;
break;
}
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
dvp.dp_timeout = (int)ms;
/* retry devpoll() with the recomputed timeout */
}
} while (1);
if (poll_result < 0) { if (poll_result < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_IOError);
...@@ -930,28 +952,26 @@ devpoll_poll(devpollObject *self, PyObject *args) ...@@ -930,28 +952,26 @@ devpoll_poll(devpollObject *self, PyObject *args)
} }
/* build the result list */ /* build the result list */
result_list = PyList_New(poll_result); result_list = PyList_New(poll_result);
if (!result_list) if (!result_list)
return NULL; return NULL;
else {
for (i = 0; i < poll_result; i++) { for (i = 0; i < poll_result; i++) {
num1 = PyLong_FromLong(self->fds[i].fd); num1 = PyLong_FromLong(self->fds[i].fd);
num2 = PyLong_FromLong(self->fds[i].revents); num2 = PyLong_FromLong(self->fds[i].revents);
if ((num1 == NULL) || (num2 == NULL)) { if ((num1 == NULL) || (num2 == NULL)) {
Py_XDECREF(num1); Py_XDECREF(num1);
Py_XDECREF(num2); Py_XDECREF(num2);
goto error; goto error;
} }
value = PyTuple_Pack(2, num1, num2); value = PyTuple_Pack(2, num1, num2);
Py_DECREF(num1); Py_DECREF(num1);
Py_DECREF(num2); Py_DECREF(num2);
if (value == NULL) if (value == NULL)
goto error; goto error;
if ((PyList_SetItem(result_list, i, value)) == -1) { if ((PyList_SetItem(result_list, i, value)) == -1) {
Py_DECREF(value); Py_DECREF(value);
goto error; goto error;
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment