Issue #23485: select.kqueue.control() is now retried when interrupted by a signal
This commit is contained in:
parent
b6faf0dfa7
commit
4448c08451
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
self.stop_alarm()
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue