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
|
- max_events must be 0 or a positive integer
|
||||||
- timeout in seconds (floats possible)
|
- 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:
|
.. _kevent-objects:
|
||||||
|
|
||||||
|
|
|
@ -621,7 +621,8 @@ Changes in the Python API
|
||||||
|
|
||||||
- :func:`os.open`, :func:`open`
|
- :func:`os.open`, :func:`open`
|
||||||
- :func:`os.read`, :func:`os.write`
|
- :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`
|
- :func:`time.sleep`
|
||||||
|
|
||||||
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
|
* 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):
|
def select(self, timeout=None):
|
||||||
timeout = None if timeout is None else max(timeout, 0)
|
timeout = None if timeout is None else max(timeout, 0)
|
||||||
max_ev = len(self._fd_to_key)
|
max_ev = len(self._fd_to_key)
|
||||||
|
kev_list = self._kqueue.control(None, max_ev, timeout)
|
||||||
|
|
||||||
ready = []
|
ready = []
|
||||||
try:
|
|
||||||
kev_list = self._kqueue.control(None, max_ev, timeout)
|
|
||||||
except InterruptedError:
|
|
||||||
return ready
|
|
||||||
for kev in kev_list:
|
for kev in kev_list:
|
||||||
fd = kev.ident
|
fd = kev.ident
|
||||||
flag = kev.filter
|
flag = kev.filter
|
||||||
|
|
|
@ -315,8 +315,8 @@ class SelectEINTRTest(EINTRBaseTest):
|
||||||
def test_select(self):
|
def test_select(self):
|
||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
select.select([], [], [], self.sleep_time)
|
select.select([], [], [], self.sleep_time)
|
||||||
self.stop_alarm()
|
|
||||||
dt = time.monotonic() - t0
|
dt = time.monotonic() - t0
|
||||||
|
self.stop_alarm()
|
||||||
self.assertGreaterEqual(dt, self.sleep_time)
|
self.assertGreaterEqual(dt, self.sleep_time)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll')
|
@unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll')
|
||||||
|
@ -325,8 +325,8 @@ class SelectEINTRTest(EINTRBaseTest):
|
||||||
|
|
||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
poller.poll(self.sleep_time * 1e3)
|
poller.poll(self.sleep_time * 1e3)
|
||||||
self.stop_alarm()
|
|
||||||
dt = time.monotonic() - t0
|
dt = time.monotonic() - t0
|
||||||
|
self.stop_alarm()
|
||||||
self.assertGreaterEqual(dt, self.sleep_time)
|
self.assertGreaterEqual(dt, self.sleep_time)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll')
|
@unittest.skipUnless(hasattr(select, 'epoll'), 'need select.epoll')
|
||||||
|
@ -336,8 +336,19 @@ class SelectEINTRTest(EINTRBaseTest):
|
||||||
|
|
||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
poller.poll(self.sleep_time)
|
poller.poll(self.sleep_time)
|
||||||
self.stop_alarm()
|
|
||||||
dt = time.monotonic() - t0
|
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)
|
self.assertGreaterEqual(dt, self.sleep_time)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2083,8 +2083,9 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
|
||||||
PyObject *result = NULL;
|
PyObject *result = NULL;
|
||||||
struct kevent *evl = NULL;
|
struct kevent *evl = NULL;
|
||||||
struct kevent *chl = NULL;
|
struct kevent *chl = NULL;
|
||||||
struct timespec timeout;
|
struct timespec timeoutspec;
|
||||||
struct timespec *ptimeoutspec;
|
struct timespec *ptimeoutspec;
|
||||||
|
_PyTime_t timeout, deadline = 0;
|
||||||
|
|
||||||
if (self->kqfd < 0)
|
if (self->kqfd < 0)
|
||||||
return kqueue_queue_err_closed();
|
return kqueue_queue_err_closed();
|
||||||
|
@ -2103,9 +2104,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
|
||||||
ptimeoutspec = NULL;
|
ptimeoutspec = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_PyTime_t ts;
|
if (_PyTime_FromSecondsObject(&timeout,
|
||||||
|
|
||||||
if (_PyTime_FromSecondsObject(&ts,
|
|
||||||
otimeout, _PyTime_ROUND_CEILING) < 0) {
|
otimeout, _PyTime_ROUND_CEILING) < 0) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"timeout argument must be an number "
|
"timeout argument must be an number "
|
||||||
|
@ -2114,15 +2113,15 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_PyTime_AsTimespec(ts, &timeout) == -1)
|
if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (timeout.tv_sec < 0) {
|
if (timeoutspec.tv_sec < 0) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"timeout must be positive or None");
|
"timeout must be positive or None");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ptimeoutspec = &timeout;
|
ptimeoutspec = &timeoutspec;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch != NULL && ch != Py_None) {
|
if (ch != NULL && ch != Py_None) {
|
||||||
|
@ -2167,10 +2166,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
if (ptimeoutspec)
|
||||||
gotevents = kevent(self->kqfd, chl, nchanges,
|
deadline = _PyTime_GetMonotonicClock() + timeout;
|
||||||
evl, nevents, ptimeoutspec);
|
|
||||||
Py_END_ALLOW_THREADS
|
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) {
|
if (gotevents == -1) {
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
|
Loading…
Reference in New Issue