mirror of
https://github.com/python/cpython.git
synced 2024-11-24 02:15:30 +08:00
Merge heads
This commit is contained in:
commit
80d84c89ee
@ -249,6 +249,12 @@ object.
|
||||
returning. If *timeout* is omitted, -1, or :const:`None`, the call will
|
||||
block until there is an event for this poll object.
|
||||
|
||||
.. 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`.
|
||||
|
||||
|
||||
.. _epoll-objects:
|
||||
|
||||
@ -454,6 +460,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:
|
||||
|
||||
|
@ -159,6 +159,12 @@ below:
|
||||
timeout has elapsed if the current process receives a signal: in this
|
||||
case, an empty list will be returned.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
The selector is now retried with a recomputed timeout when interrupted
|
||||
by a signal if the signal handler did not raise an exception (see
|
||||
:pep:`475` for the rationale), instead of returning an empty list
|
||||
of events before the timeout.
|
||||
|
||||
.. method:: close()
|
||||
|
||||
Close the selector.
|
||||
|
@ -619,9 +619,10 @@ Changes in the Python API
|
||||
instead of raising :exc:`InterruptedError` if the signal handler does not
|
||||
raise an exception:
|
||||
|
||||
- :func:`os.open`, :func:`open`
|
||||
- :func:`open`, :func:`os.open`, :func:`io.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:`select.devpoll.poll`
|
||||
- :func:`time.sleep`
|
||||
|
||||
* Before Python 3.5, a :class:`datetime.time` object was considered to be false
|
||||
|
@ -479,11 +479,10 @@ if hasattr(select, 'devpoll'):
|
||||
# devpoll() has a resolution of 1 millisecond, round away from
|
||||
# zero to wait *at least* timeout seconds.
|
||||
timeout = math.ceil(timeout * 1e3)
|
||||
|
||||
fd_event_list = self._devpoll.poll(timeout)
|
||||
|
||||
ready = []
|
||||
try:
|
||||
fd_event_list = self._devpoll.poll(timeout)
|
||||
except InterruptedError:
|
||||
return ready
|
||||
for fd, event in fd_event_list:
|
||||
events = 0
|
||||
if event & ~select.POLLIN:
|
||||
@ -549,11 +548,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,30 @@ 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)
|
||||
|
||||
@unittest.skipUnless(hasattr(select, 'devpoll'), 'need select.devpoll')
|
||||
def test_devpoll(self):
|
||||
poller = select.devpoll()
|
||||
self.addCleanup(poller.close)
|
||||
|
||||
t0 = time.monotonic()
|
||||
poller.poll(self.sleep_time * 1e3)
|
||||
dt = time.monotonic() - t0
|
||||
self.stop_alarm()
|
||||
self.assertGreaterEqual(dt, self.sleep_time)
|
||||
|
||||
|
||||
|
@ -357,7 +357,35 @@ class BaseSelectorTestCase(unittest.TestCase):
|
||||
|
||||
@unittest.skipUnless(hasattr(signal, "alarm"),
|
||||
"signal.alarm() required for this test")
|
||||
def test_select_interrupt(self):
|
||||
def test_select_interrupt_exc(self):
|
||||
s = self.SELECTOR()
|
||||
self.addCleanup(s.close)
|
||||
|
||||
rd, wr = self.make_socketpair()
|
||||
|
||||
class InterruptSelect(Exception):
|
||||
pass
|
||||
|
||||
def handler(*args):
|
||||
raise InterruptSelect
|
||||
|
||||
orig_alrm_handler = signal.signal(signal.SIGALRM, handler)
|
||||
self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
|
||||
self.addCleanup(signal.alarm, 0)
|
||||
|
||||
signal.alarm(1)
|
||||
|
||||
s.register(rd, selectors.EVENT_READ)
|
||||
t = time()
|
||||
# select() is interrupted by a signal which raises an exception
|
||||
with self.assertRaises(InterruptSelect):
|
||||
s.select(30)
|
||||
# select() was interrupted before the timeout of 30 seconds
|
||||
self.assertLess(time() - t, 5.0)
|
||||
|
||||
@unittest.skipUnless(hasattr(signal, "alarm"),
|
||||
"signal.alarm() required for this test")
|
||||
def test_select_interrupt_noraise(self):
|
||||
s = self.SELECTOR()
|
||||
self.addCleanup(s.close)
|
||||
|
||||
@ -371,8 +399,11 @@ class BaseSelectorTestCase(unittest.TestCase):
|
||||
|
||||
s.register(rd, selectors.EVENT_READ)
|
||||
t = time()
|
||||
self.assertFalse(s.select(2))
|
||||
self.assertLess(time() - t, 2.5)
|
||||
# select() is interrupted by a signal, but the signal handler doesn't
|
||||
# raise an exception, so select() should by retries with a recomputed
|
||||
# timeout
|
||||
self.assertFalse(s.select(1.5))
|
||||
self.assertGreaterEqual(time() - t, 1.0)
|
||||
|
||||
|
||||
class ScalableSelectorMixIn:
|
||||
|
@ -876,40 +876,38 @@ static PyObject *
|
||||
devpoll_poll(devpollObject *self, PyObject *args)
|
||||
{
|
||||
struct dvpoll dvp;
|
||||
PyObject *result_list = NULL, *tout = NULL;
|
||||
PyObject *result_list = NULL, *timeout_obj = NULL;
|
||||
int poll_result, i;
|
||||
long timeout;
|
||||
PyObject *value, *num1, *num2;
|
||||
_PyTime_t timeout, ms, deadline = 0;
|
||||
|
||||
if (self->fd_devpoll < 0)
|
||||
return devpoll_err_closed();
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
|
||||
if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check values for timeout */
|
||||
if (tout == NULL || tout == Py_None)
|
||||
if (timeout_obj == NULL || timeout_obj == Py_None) {
|
||||
timeout = -1;
|
||||
else if (!PyNumber_Check(tout)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"timeout must be an integer or None");
|
||||
return NULL;
|
||||
ms = -1;
|
||||
}
|
||||
else {
|
||||
tout = PyNumber_Long(tout);
|
||||
if (!tout)
|
||||
if (_PyTime_FromMillisecondsObject(&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;
|
||||
timeout = PyLong_AsLong(tout);
|
||||
Py_DECREF(tout);
|
||||
if (timeout == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((timeout < -1) || (timeout > INT_MAX)) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"timeout is out of range");
|
||||
return NULL;
|
||||
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
||||
if (ms < -1 || ms > INT_MAX) {
|
||||
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (devpoll_flush(self))
|
||||
@ -917,12 +915,36 @@ devpoll_poll(devpollObject *self, PyObject *args)
|
||||
|
||||
dvp.dp_fds = self->fds;
|
||||
dvp.dp_nfds = self->max_n_fds;
|
||||
dvp.dp_timeout = timeout;
|
||||
dvp.dp_timeout = (int)ms;
|
||||
|
||||
/* call devpoll() */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (timeout >= 0)
|
||||
deadline = _PyTime_GetMonotonicClock() + timeout;
|
||||
|
||||
do {
|
||||
/* call devpoll() */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
errno = 0;
|
||||
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
|
||||
/* devpoll() was interrupted by a signal */
|
||||
if (PyErr_CheckSignals())
|
||||
return NULL;
|
||||
|
||||
if (timeout >= 0) {
|
||||
timeout = deadline - _PyTime_GetMonotonicClock();
|
||||
if (timeout < 0) {
|
||||
poll_result = 0;
|
||||
break;
|
||||
}
|
||||
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
||||
dvp.dp_timeout = (int)ms;
|
||||
/* retry devpoll() with the recomputed timeout */
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if (poll_result < 0) {
|
||||
PyErr_SetFromErrno(PyExc_IOError);
|
||||
@ -930,28 +952,26 @@ devpoll_poll(devpollObject *self, PyObject *args)
|
||||
}
|
||||
|
||||
/* build the result list */
|
||||
|
||||
result_list = PyList_New(poll_result);
|
||||
if (!result_list)
|
||||
return NULL;
|
||||
else {
|
||||
for (i = 0; i < poll_result; i++) {
|
||||
num1 = PyLong_FromLong(self->fds[i].fd);
|
||||
num2 = PyLong_FromLong(self->fds[i].revents);
|
||||
if ((num1 == NULL) || (num2 == NULL)) {
|
||||
Py_XDECREF(num1);
|
||||
Py_XDECREF(num2);
|
||||
goto error;
|
||||
}
|
||||
value = PyTuple_Pack(2, num1, num2);
|
||||
Py_DECREF(num1);
|
||||
Py_DECREF(num2);
|
||||
if (value == NULL)
|
||||
goto error;
|
||||
if ((PyList_SetItem(result_list, i, value)) == -1) {
|
||||
Py_DECREF(value);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < poll_result; i++) {
|
||||
num1 = PyLong_FromLong(self->fds[i].fd);
|
||||
num2 = PyLong_FromLong(self->fds[i].revents);
|
||||
if ((num1 == NULL) || (num2 == NULL)) {
|
||||
Py_XDECREF(num1);
|
||||
Py_XDECREF(num2);
|
||||
goto error;
|
||||
}
|
||||
value = PyTuple_Pack(2, num1, num2);
|
||||
Py_DECREF(num1);
|
||||
Py_DECREF(num2);
|
||||
if (value == NULL)
|
||||
goto error;
|
||||
if ((PyList_SetItem(result_list, i, value)) == -1) {
|
||||
Py_DECREF(value);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2083,8 +2103,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 +2124,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 +2133,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 +2186,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
Block a user