summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/select.rst6
-rw-r--r--Doc/whatsnew/3.5.rst4
-rw-r--r--Lib/selectors.py7
-rw-r--r--Lib/test/eintrdata/eintr_tester.py11
-rw-r--r--Modules/selectmodule.c106
5 files changed, 85 insertions, 49 deletions
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index e6f95fd..a62dc84 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -249,6 +249,12 @@ object.
returning. If *timeout* is omitted, -1, or :const:`None`, the call will
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:
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index e5fefae..434c0a7 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -619,10 +619,10 @@ Changes in the Python API
instead of raising :exc:`InterruptedError` if the signal handler does not
raise an exception:
- - :func:`os.open`, :func:`open`
+ - :func:`open`, :func:`os.open`, :func:`io.open`
- :func:`os.read`, :func:`os.write`
- :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`
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
diff --git a/Lib/selectors.py b/Lib/selectors.py
index a3f2e78..44a6150 100644
--- a/Lib/selectors.py
+++ b/Lib/selectors.py
@@ -479,11 +479,10 @@ if hasattr(select, 'devpoll'):
# devpoll() has a resolution of 1 millisecond, round away from
# zero to wait *at least* timeout seconds.
timeout = math.ceil(timeout * 1e3)
+
+ fd_event_list = self._devpoll.poll(timeout)
+
ready = []
- try:
- fd_event_list = self._devpoll.poll(timeout)
- except InterruptedError:
- return ready
for fd, event in fd_event_list:
events = 0
if event & ~select.POLLIN:
diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py
index 4e9b992..f755880 100644
--- a/Lib/test/eintrdata/eintr_tester.py
+++ b/Lib/test/eintrdata/eintr_tester.py
@@ -351,6 +351,17 @@ class SelectEINTRTest(EINTRBaseTest):
self.stop_alarm()
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():
support.run_unittest(
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 452525d..590aeca 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -876,40 +876,38 @@ static PyObject *
devpoll_poll(devpollObject *self, PyObject *args)
{
struct dvpoll dvp;
- PyObject *result_list = NULL, *tout = NULL;
+ PyObject *result_list = NULL, *timeout_obj = NULL;
int poll_result, i;
- long timeout;
PyObject *value, *num1, *num2;
+ _PyTime_t timeout, ms, deadline = 0;
if (self->fd_devpoll < 0)
return devpoll_err_closed();
- if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
+ if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL;
}
/* Check values for timeout */
- if (tout == NULL || tout == Py_None)
+ if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
- else if (!PyNumber_Check(tout)) {
- PyErr_SetString(PyExc_TypeError,
- "timeout must be an integer or None");
- return NULL;
+ ms = -1;
}
else {
- tout = PyNumber_Long(tout);
- if (!tout)
- return NULL;
- timeout = PyLong_AsLong(tout);
- Py_DECREF(tout);
- if (timeout == -1 && PyErr_Occurred())
+ if (_PyTime_FromMillisecondsObject(&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;
- }
+ }
- if ((timeout < -1) || (timeout > INT_MAX)) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout is out of range");
- return NULL;
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ if (ms < -1 || ms > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "timeout is too large");
+ return NULL;
+ }
}
if (devpoll_flush(self))
@@ -917,12 +915,36 @@ devpoll_poll(devpollObject *self, PyObject *args)
dvp.dp_fds = self->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() */
- Py_BEGIN_ALLOW_THREADS
- poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
- Py_END_ALLOW_THREADS
+ /* 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) {
PyErr_SetFromErrno(PyExc_IOError);
@@ -930,28 +952,26 @@ devpoll_poll(devpollObject *self, PyObject *args)
}
/* build the result list */
-
result_list = PyList_New(poll_result);
if (!result_list)
return NULL;
- else {
- for (i = 0; i < poll_result; i++) {
- num1 = PyLong_FromLong(self->fds[i].fd);
- num2 = PyLong_FromLong(self->fds[i].revents);
- if ((num1 == NULL) || (num2 == NULL)) {
- Py_XDECREF(num1);
- Py_XDECREF(num2);
- goto error;
- }
- value = PyTuple_Pack(2, num1, num2);
- Py_DECREF(num1);
- Py_DECREF(num2);
- if (value == NULL)
- goto error;
- if ((PyList_SetItem(result_list, i, value)) == -1) {
- Py_DECREF(value);
- goto error;
- }
+
+ for (i = 0; i < poll_result; i++) {
+ num1 = PyLong_FromLong(self->fds[i].fd);
+ num2 = PyLong_FromLong(self->fds[i].revents);
+ if ((num1 == NULL) || (num2 == NULL)) {
+ Py_XDECREF(num1);
+ Py_XDECREF(num2);
+ goto error;
+ }
+ value = PyTuple_Pack(2, num1, num2);
+ Py_DECREF(num1);
+ Py_DECREF(num2);
+ if (value == NULL)
+ goto error;
+ if ((PyList_SetItem(result_list, i, value)) == -1) {
+ Py_DECREF(value);
+ goto error;
}
}