Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 45ca48b

Browse files
committed
Issue #23485: select.devpoll.poll() is now retried when interrupted by a signal
1 parent 4448c08 commit 45ca48b

5 files changed

Lines changed: 85 additions & 49 deletions

File tree

Doc/library/select.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ object.
249249
returning. If *timeout* is omitted, -1, or :const:`None`, the call will
250250
block until there is an event for this poll object.
251251

252+
.. versionchanged:: 3.5
253+
The function is now retried with a recomputed timeout when interrupted by
254+
a signal, except if the signal handler raises an exception (see
255+
:pep:`475` for the rationale), instead of raising
256+
:exc:`InterruptedError`.
257+
252258

253259
.. _epoll-objects:
254260

Doc/whatsnew/3.5.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,10 @@ Changes in the Python API
619619
instead of raising :exc:`InterruptedError` if the signal handler does not
620620
raise an exception:
621621

622-
- :func:`os.open`, :func:`open`
622+
- :func:`open`, :func:`os.open`, :func:`io.open`
623623
- :func:`os.read`, :func:`os.write`
624624
- :func:`select.select`, :func:`select.poll.poll`, :func:`select.epoll.poll`,
625-
:func:`select.kqueue.control`
625+
:func:`select.kqueue.control`, :func:`select.devpoll.poll`
626626
- :func:`time.sleep`
627627

628628
* Before Python 3.5, a :class:`datetime.time` object was considered to be false

Lib/selectors.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,11 +479,10 @@ def select(self, timeout=None):
479479
# devpoll() has a resolution of 1 millisecond, round away from
480480
# zero to wait *at least* timeout seconds.
481481
timeout = math.ceil(timeout * 1e3)
482+
483+
fd_event_list = self._devpoll.poll(timeout)
484+
482485
ready = []
483-
try:
484-
fd_event_list = self._devpoll.poll(timeout)
485-
except InterruptedError:
486-
return ready
487486
for fd, event in fd_event_list:
488487
events = 0
489488
if event & ~select.POLLIN:

Lib/test/eintrdata/eintr_tester.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,17 @@ def test_kqueue(self):
351351
self.stop_alarm()
352352
self.assertGreaterEqual(dt, self.sleep_time)
353353

354+
@unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll')
355+
def test_devpoll(self):
356+
poller = select.devpoll()
357+
self.addCleanup(poller.close)
358+
359+
t0 = time.monotonic()
360+
poller.poll(self.sleep_time * 1e3)
361+
dt = time.monotonic() - t0
362+
self.stop_alarm()
363+
self.assertGreaterEqual(dt, self.sleep_time)
364+
354365

355366
def test_main():
356367
support.run_unittest(

Modules/selectmodule.c

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -876,82 +876,102 @@ static PyObject *
876876
devpoll_poll(devpollObject *self, PyObject *args)
877877
{
878878
struct dvpoll dvp;
879-
PyObject *result_list = NULL, *tout = NULL;
879+
PyObject *result_list = NULL, *timeout_obj = NULL;
880880
int poll_result, i;
881-
long timeout;
882881
PyObject *value, *num1, *num2;
882+
_PyTime_t timeout, ms, deadline = 0;
883883

884884
if (self->fd_devpoll < 0)
885885
return devpoll_err_closed();
886886

887-
if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
887+
if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
888888
return NULL;
889889
}
890890

891891
/* Check values for timeout */
892-
if (tout == NULL || tout == Py_None)
892+
if (timeout_obj == NULL || timeout_obj == Py_None) {
893893
timeout = -1;
894-
else if (!PyNumber_Check(tout)) {
895-
PyErr_SetString(PyExc_TypeError,
896-
"timeout must be an integer or None");
897-
return NULL;
894+
ms = -1;
898895
}
899896
else {
900-
tout = PyNumber_Long(tout);
901-
if (!tout)
902-
return NULL;
903-
timeout = PyLong_AsLong(tout);
904-
Py_DECREF(tout);
905-
if (timeout == -1 && PyErr_Occurred())
897+
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
898+
_PyTime_ROUND_CEILING) < 0) {
899+
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
900+
PyErr_SetString(PyExc_TypeError,
901+
"timeout must be an integer or None");
902+
}
906903
return NULL;
907-
}
904+
}
908905

909-
if ((timeout < -1) || (timeout > INT_MAX)) {
910-
PyErr_SetString(PyExc_OverflowError,
911-
"timeout is out of range");
912-
return NULL;
906+
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
907+
if (ms < -1 || ms > INT_MAX) {
908+
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
909+
return NULL;
910+
}
913911
}
914912

915913
if (devpoll_flush(self))
916914
return NULL;
917915

918916
dvp.dp_fds = self->fds;
919917
dvp.dp_nfds = self->max_n_fds;
920-
dvp.dp_timeout = timeout;
918+
dvp.dp_timeout = (int)ms;
919+
920+
if (timeout >= 0)
921+
deadline = _PyTime_GetMonotonicClock() + timeout;
922+
923+
do {
924+
/* call devpoll() */
925+
Py_BEGIN_ALLOW_THREADS
926+
errno = 0;
927+
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
928+
Py_END_ALLOW_THREADS
929+
930+
if (errno != EINTR)
931+
break;
921932

922-
/* call devpoll() */
923-
Py_BEGIN_ALLOW_THREADS
924-
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
925-
Py_END_ALLOW_THREADS
933+
/* devpoll() was interrupted by a signal */
934+
if (PyErr_CheckSignals())
935+
return NULL;
936+
937+
if (timeout >= 0) {
938+
timeout = deadline - _PyTime_GetMonotonicClock();
939+
if (timeout < 0) {
940+
poll_result = 0;
941+
break;
942+
}
943+
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
944+
dvp.dp_timeout = (int)ms;
945+
/* retry devpoll() with the recomputed timeout */
946+
}
947+
} while (1);
926948

927949
if (poll_result < 0) {
928950
PyErr_SetFromErrno(PyExc_IOError);
929951
return NULL;
930952
}
931953

932954
/* build the result list */
933-
934955
result_list = PyList_New(poll_result);
935956
if (!result_list)
936957
return NULL;
937-
else {
938-
for (i = 0; i < poll_result; i++) {
939-
num1 = PyLong_FromLong(self->fds[i].fd);
940-
num2 = PyLong_FromLong(self->fds[i].revents);
941-
if ((num1 == NULL) || (num2 == NULL)) {
942-
Py_XDECREF(num1);
943-
Py_XDECREF(num2);
944-
goto error;
945-
}
946-
value = PyTuple_Pack(2, num1, num2);
947-
Py_DECREF(num1);
948-
Py_DECREF(num2);
949-
if (value == NULL)
950-
goto error;
951-
if ((PyList_SetItem(result_list, i, value)) == -1) {
952-
Py_DECREF(value);
953-
goto error;
954-
}
958+
959+
for (i = 0; i < poll_result; i++) {
960+
num1 = PyLong_FromLong(self->fds[i].fd);
961+
num2 = PyLong_FromLong(self->fds[i].revents);
962+
if ((num1 == NULL) || (num2 == NULL)) {
963+
Py_XDECREF(num1);
964+
Py_XDECREF(num2);
965+
goto error;
966+
}
967+
value = PyTuple_Pack(2, num1, num2);
968+
Py_DECREF(num1);
969+
Py_DECREF(num2);
970+
if (value == NULL)
971+
goto error;
972+
if ((PyList_SetItem(result_list, i, value)) == -1) {
973+
Py_DECREF(value);
974+
goto error;
955975
}
956976
}
957977

0 commit comments

Comments
 (0)