]> granicus.if.org Git - python/commitdiff
Issue #23485: select.kqueue.control() is now retried when interrupted by a signal
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 31 Mar 2015 09:48:34 +0000 (11:48 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 31 Mar 2015 09:48:34 +0000 (11:48 +0200)
Doc/library/select.rst
Doc/whatsnew/3.5.rst
Lib/selectors.py
Lib/test/eintrdata/eintr_tester.py
Modules/selectmodule.c

index 61f3835a6c79e144edb1f66b7b2f86203d628261..e6f95fdafe797bb9121ca4619efc0d3723ea748b 100644 (file)
@@ -454,6 +454,12 @@ Kqueue Objects
    - max_events must be 0 or a positive integer
    - timeout in seconds (floats possible)
 
+   .. 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`.
+
 
 .. _kevent-objects:
 
index 6c2d5213a4ddf0de91ae6278f59cb58d119bc539..e5fefaeb0485ba1090d86de6f1170e713fd45aa0 100644 (file)
@@ -621,7 +621,8 @@ Changes in the Python API
 
   - :func:`os.open`, :func:`open`
   - :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:`time.sleep`
 
 * Before Python 3.5, a :class:`datetime.time` object was considered to be false
index 2a0a44c9248add131f9fa19228fb2455723293c4..a3f2e78c24fb0fad6e3a6c5b4a52536853823447 100644 (file)
@@ -549,11 +549,9 @@ if hasattr(select, 'kqueue'):
         def select(self, timeout=None):
             timeout = None if timeout is None else max(timeout, 0)
             max_ev = len(self._fd_to_key)
+            kev_list = self._kqueue.control(None, max_ev, timeout)
+
             ready = []
-            try:
-                kev_list = self._kqueue.control(None, max_ev, timeout)
-            except InterruptedError:
-                return ready
             for kev in kev_list:
                 fd = kev.ident
                 flag = kev.filter
index 0df9762cfc16ebd746e05d456a128e9fd5a3fb9b..4e9b992868e29529e46fc47a03fd6a85d4acd84f 100644 (file)
@@ -315,8 +315,8 @@ class SelectEINTRTest(EINTRBaseTest):
     def test_select(self):
         t0 = time.monotonic()
         select.select([], [], [], self.sleep_time)
-        self.stop_alarm()
         dt = time.monotonic() - t0
+        self.stop_alarm()
         self.assertGreaterEqual(dt, self.sleep_time)
 
     @unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll')
@@ -325,8 +325,8 @@ class SelectEINTRTest(EINTRBaseTest):
 
         t0 = time.monotonic()
         poller.poll(self.sleep_time * 1e3)
-        self.stop_alarm()
         dt = time.monotonic() - t0
+        self.stop_alarm()
         self.assertGreaterEqual(dt, self.sleep_time)
 
     @unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll')
@@ -336,8 +336,19 @@ class SelectEINTRTest(EINTRBaseTest):
 
         t0 = time.monotonic()
         poller.poll(self.sleep_time)
+        dt = time.monotonic() - t0
         self.stop_alarm()
+        self.assertGreaterEqual(dt, self.sleep_time)
+
+    @unittest.skipUnless(hasattr(select, 'kqueue'), 'need select.kqueue')
+    def test_kqueue(self):
+        kqueue = select.kqueue()
+        self.addCleanup(kqueue.close)
+
+        t0 = time.monotonic()
+        kqueue.control(None, 1, self.sleep_time)
         dt = time.monotonic() - t0
+        self.stop_alarm()
         self.assertGreaterEqual(dt, self.sleep_time)
 
 
index 96be5ba40f3f4c79adfb9f82472fb4612c6c6cd5..452525d5a01ddd1fe228f6fd2d0c6b8eec8b1405 100644 (file)
@@ -2083,8 +2083,9 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
     PyObject *result = NULL;
     struct kevent *evl = NULL;
     struct kevent *chl = NULL;
-    struct timespec timeout;
+    struct timespec timeoutspec;
     struct timespec *ptimeoutspec;
+    _PyTime_t timeout, deadline = 0;
 
     if (self->kqfd < 0)
         return kqueue_queue_err_closed();
@@ -2103,9 +2104,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
         ptimeoutspec = NULL;
     }
     else {
-        _PyTime_t ts;
-
-        if (_PyTime_FromSecondsObject(&ts,
+        if (_PyTime_FromSecondsObject(&timeout,
                                       otimeout, _PyTime_ROUND_CEILING) < 0) {
             PyErr_Format(PyExc_TypeError,
                 "timeout argument must be an number "
@@ -2114,15 +2113,15 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
             return NULL;
         }
 
-        if (_PyTime_AsTimespec(ts, &timeout) == -1)
+        if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
             return NULL;
 
-        if (timeout.tv_sec < 0) {
+        if (timeoutspec.tv_sec < 0) {
             PyErr_SetString(PyExc_ValueError,
                             "timeout must be positive or None");
             return NULL;
         }
-        ptimeoutspec = &timeout;
+        ptimeoutspec = &timeoutspec;
     }
 
     if (ch != NULL && ch != Py_None) {
@@ -2167,10 +2166,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
         }
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    gotevents = kevent(self->kqfd, chl, nchanges,
-                       evl, nevents, ptimeoutspec);
-    Py_END_ALLOW_THREADS
+    if (ptimeoutspec)
+        deadline = _PyTime_GetMonotonicClock() + timeout;
+
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        errno = 0;
+        gotevents = kevent(self->kqfd, chl, nchanges,
+                           evl, nevents, ptimeoutspec);
+        Py_END_ALLOW_THREADS
+
+        if (errno != EINTR)
+            break;
+
+        /* kevent() was interrupted by a signal */
+        if (PyErr_CheckSignals())
+            goto error;
+
+        if (ptimeoutspec) {
+            timeout = deadline - _PyTime_GetMonotonicClock();
+            if (timeout < 0) {
+                gotevents = 0;
+                break;
+            }
+            if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
+                goto error;
+            /* retry kevent() with the recomputed timeout */
+        }
+    } while (1);
 
     if (gotevents == -1) {
         PyErr_SetFromErrno(PyExc_OSError);