[Python-checkins] cpython: Issue #23485: select.epoll.poll() is now retried when interrupted by a signal
victor.stinner
python-checkins at python.org
Mon Mar 30 22:15:59 CEST 2015
https://hg.python.org/cpython/rev/5194a84ed9f3
changeset: 95308:5194a84ed9f3
user: Victor Stinner <victor.stinner at gmail.com>
date: Mon Mar 30 21:59:21 2015 +0200
summary:
Issue #23485: select.epoll.poll() is now retried when interrupted by a signal
files:
Doc/library/select.rst | 6 +
Doc/whatsnew/3.5.rst | 2 +-
Lib/selectors.py | 6 +-
Lib/test/eintrdata/eintr_tester.py | 11 ++
Modules/selectmodule.c | 69 +++++++++++++----
5 files changed, 71 insertions(+), 23 deletions(-)
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -329,6 +329,12 @@
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:
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -621,7 +621,7 @@
- :func:`os.open`, :func:`open`
- :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`
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
diff --git a/Lib/selectors.py b/Lib/selectors.py
--- a/Lib/selectors.py
+++ b/Lib/selectors.py
@@ -423,11 +423,9 @@
# FD is registered.
max_ev = max(len(self._fd_to_key), 1)
+ fd_event_list = self._epoll.poll(timeout, max_ev)
+
ready = []
- try:
- fd_event_list = self._epoll.poll(timeout, max_ev)
- except InterruptedError:
- return ready
for fd, event in fd_event_list:
events = 0
if event & ~select.EPOLLIN:
diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py
--- a/Lib/test/eintrdata/eintr_tester.py
+++ b/Lib/test/eintrdata/eintr_tester.py
@@ -329,6 +329,17 @@
dt = time.monotonic() - t0
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():
support.run_unittest(
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -535,7 +535,7 @@
if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
ms = -1;
- deadline = 0;
+ deadline = 0; /* initialize to prevent gcc warning */
}
else {
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
@@ -1465,34 +1465,46 @@
static PyObject *
pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
{
- double dtimeout = -1.;
- int timeout;
+ static char *kwlist[] = {"timeout", "maxevents", NULL};
+ PyObject *timeout_obj = NULL;
int maxevents = -1;
int nfds, i;
PyObject *elist = NULL, *etuple = NULL;
struct epoll_event *evs = NULL;
- static char *kwlist[] = {"timeout", "maxevents", NULL};
+ _PyTime_t timeout, ms, deadline;
if (self->epfd < 0)
return pyepoll_err_closed();
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|di:poll", kwlist,
- &dtimeout, &maxevents)) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:poll", kwlist,
+ &timeout_obj, &maxevents)) {
return NULL;
}
- if (dtimeout < 0) {
+ if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
- }
- else if (dtimeout * 1000.0 > INT_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout is too large");
- return NULL;
+ ms = -1;
+ deadline = 0; /* initialize to prevent gcc warning */
}
else {
- /* epoll_wait() has a resolution of 1 millisecond, round away from zero
- to wait *at least* dtimeout seconds. */
- timeout = (int)ceil(dtimeout * 1000.0);
+ /* epoll_wait() has a resolution of 1 millisecond, round towards
+ infinity to wait at least timeout seconds. */
+ 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) {
@@ -1511,9 +1523,30 @@
return NULL;
}
- Py_BEGIN_ALLOW_THREADS
- nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
- Py_END_ALLOW_THREADS
+ do {
+ Py_BEGIN_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) {
PyErr_SetFromErrno(PyExc_OSError);
goto error;
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list