summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/select.rst6
-rw-r--r--Doc/whatsnew/3.5.rst3
-rw-r--r--Lib/selectors.py6
-rw-r--r--Lib/test/eintrdata/eintr_tester.py15
-rw-r--r--Modules/selectmodule.c45
5 files changed, 57 insertions, 18 deletions
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index 61f3835..e6f95fd 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -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:
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 6c2d521..e5fefae 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -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
diff --git a/Lib/selectors.py b/Lib/selectors.py
index 2a0a44c..a3f2e78 100644
--- a/Lib/selectors.py
+++ b/Lib/selectors.py
@@ -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
diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py
index 0df9762..4e9b992 100644
--- a/Lib/test/eintrdata/eintr_tester.py
+++ b/Lib/test/eintrdata/eintr_tester.py
@@ -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)
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 96be5ba..452525d 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -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);