[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