mirror of
https://github.com/python/cpython.git
synced 2025-09-19 07:00:59 +00:00
Issue #23485: select.devpoll.poll() is now retried when interrupted by a signal
This commit is contained in:
parent
4448c08451
commit
45ca48b03d
5 changed files with 85 additions and 49 deletions
|
@ -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
|
||||||
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'):
|
||||||
# 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)
|
||||||
ready = []
|
|
||||||
try:
|
|
||||||
fd_event_list = self._devpoll.poll(timeout)
|
fd_event_list = self._devpoll.poll(timeout)
|
||||||
except InterruptedError:
|
|
||||||
return ready
|
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):
|
||||||
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,65 +876,86 @@ 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))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
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() */
|
/* call devpoll() */
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
errno = 0;
|
||||||
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
|
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if (errno != EINTR)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* devpoll() was interrupted by a signal */
|
||||||
|
if (PyErr_CheckSignals())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
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);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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);
|
||||||
|
@ -953,7 +974,6 @@ devpoll_poll(devpollObject *self, PyObject *args)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result_list;
|
return result_list;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue