cpython: 69b1683ee001 (original) (raw)
Mercurial > cpython
changeset 95307:69b1683ee001
Issue #23485: select.poll.poll() is now retried when interrupted by a signal [#23485]
Victor Stinner victor.stinner@gmail.com | |
---|---|
date | Mon, 30 Mar 2015 21:38:00 +0200 |
parents | 0591cf5c9ebd |
children | 5194a84ed9f3 |
files | Doc/library/select.rst Doc/whatsnew/3.5.rst Lib/asyncore.py Lib/selectors.py Lib/test/eintrdata/eintr_tester.py Modules/selectmodule.c |
diffstat | 6 files changed, 111 insertions(+), 61 deletions(-)[+] [-] Doc/library/select.rst 6 Doc/whatsnew/3.5.rst 2 Lib/asyncore.py 6 Lib/selectors.py 7 Lib/test/eintrdata/eintr_tester.py 20 Modules/selectmodule.c 131 |
line wrap: on
line diff
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -408,6 +408,12 @@ linearly scanned again. :c:func:select
returning. If timeout is omitted, negative, 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[](#l1.8)
a signal, except if the signal handler raises an exception (see[](#l1.9)
:pep:`475` for the rationale), instead of raising[](#l1.10)
:exc:`InterruptedError`.[](#l1.11)
--- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -621,7 +621,7 @@ Changes in the Python API
--- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -179,10 +179,8 @@ def poll2(timeout=0.0, map=None): flags |= select.POLLOUT if flags: pollster.register(fd, flags)
try:[](#l3.7)
r = pollster.poll(timeout)[](#l3.8)
except InterruptedError:[](#l3.9)
r = [][](#l3.10)
r = pollster.poll(timeout)[](#l3.12) for fd, flags in r:[](#l3.13) obj = map.get(fd)[](#l3.14) if obj is None:[](#l3.15)
--- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -359,11 +359,10 @@ if hasattr(select, 'poll'): # poll() 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._poll.poll(timeout)[](#l4.8)
try:[](#l4.11)
fd_event_list = self._poll.poll(timeout)[](#l4.12)
except InterruptedError:[](#l4.13)
return ready[](#l4.14) for fd, event in fd_event_list:[](#l4.15) events = 0[](#l4.16) if event & ~select.POLLIN:[](#l4.17)
--- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -38,8 +38,12 @@ class EINTRBaseTest(unittest.TestCase): cls.signal_period) @classmethod
signal.setitimer(signal.ITIMER_REAL, 0, 0)[](#l5.12)
cls.stop_alarm()[](#l5.13) signal.signal(signal.SIGALRM, cls.orig_handler)[](#l5.14)
@classmethod @@ -260,7 +264,7 @@ class TimeEINTRTest(EINTRBaseTest): def test_sleep(self): t0 = time.monotonic() time.sleep(self.sleep_time)
signal.alarm(0)[](#l5.21)
self.stop_alarm()[](#l5.22) dt = time.monotonic() - t0[](#l5.23) self.assertGreaterEqual(dt, self.sleep_time)[](#l5.24)
@@ -311,7 +315,17 @@ class SelectEINTRTest(EINTRBaseTest): def test_select(self): t0 = time.monotonic() select.select([], [], [], self.sleep_time)
signal.alarm(0)[](#l5.30)
self.stop_alarm()[](#l5.31)
dt = time.monotonic() - t0[](#l5.32)
self.assertGreaterEqual(dt, self.sleep_time)[](#l5.33)
- @unittest.skipUnless(hasattr(select, 'poll'), 'need select.poll')
- def test_poll(self):
poller = select.poll()[](#l5.37)
t0 = time.monotonic()[](#l5.39)
poller.poll(self.sleep_time * 1e3)[](#l5.40)
self.stop_alarm()[](#l5.41) dt = time.monotonic() - t0[](#l5.42) self.assertGreaterEqual(dt, self.sleep_time)[](#l5.43)
--- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -279,6 +279,7 @@ select_select(PyObject *self, PyObject * break; } _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
} while (1); @@ -520,30 +521,39 @@ any descriptors that have events or erro static PyObject * poll_poll(pollObject *self, PyObject *args) {/* retry select() with the recomputed timeout */[](#l6.7) }[](#l6.8)
- PyObject *result_list = NULL, *timeout_obj = NULL;
- int poll_result, i, j; PyObject *value = NULL, *num = NULL;
- _PyTime_t timeout, ms, deadline;
- int async_err = 0;
- if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) { return NULL; } /* Check values for timeout */
- else if (!PyNumber_Check(tout)) {
PyErr_SetString(PyExc_TypeError,[](#l6.33)
"timeout must be an integer or None");[](#l6.34)
return NULL;[](#l6.35)
tout = PyNumber_Long(tout);[](#l6.40)
if (!tout)[](#l6.41)
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,[](#l6.42)
_PyTime_ROUND_CEILING) < 0) {[](#l6.43)
if (PyErr_ExceptionMatches(PyExc_TypeError)) {[](#l6.44)
PyErr_SetString(PyExc_TypeError,[](#l6.45)
"timeout must be an integer or None");[](#l6.46)
}[](#l6.47) return NULL;[](#l6.48)
timeout = _PyLong_AsInt(tout);[](#l6.49)
Py_DECREF(tout);[](#l6.50)
if (timeout == -1 && PyErr_Occurred())[](#l6.51)
}[](#l6.52)
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);[](#l6.54)
if (ms < INT_MIN || ms > INT_MAX) {[](#l6.55)
PyErr_SetString(PyExc_OverflowError, "timeout is too large");[](#l6.56) return NULL;[](#l6.57)
}[](#l6.58)
} /* Avoid concurrent poll() invocation, issue 8865 */ @@ -561,14 +571,38 @@ poll_poll(pollObject *self, PyObject ar self->poll_running = 1; / call poll() */deadline = _PyTime_GetMonotonicClock() + timeout;[](#l6.60)
- async_err = 0;
- do {
Py_BEGIN_ALLOW_THREADS[](#l6.73)
errno = 0;[](#l6.74)
poll_result = poll(self->ufds, self->ufd_len, (int)ms);[](#l6.75)
Py_END_ALLOW_THREADS[](#l6.76)
if (errno != EINTR)[](#l6.78)
break;[](#l6.79)
/* poll() was interrupted by a signal */[](#l6.81)
if (PyErr_CheckSignals()) {[](#l6.82)
async_err = 1;[](#l6.83)
break;[](#l6.84)
}[](#l6.85)
if (timeout >= 0) {[](#l6.87)
timeout = deadline - _PyTime_GetMonotonicClock();[](#l6.88)
if (timeout < 0) {[](#l6.89)
poll_result = 0;[](#l6.90)
break;[](#l6.91)
}[](#l6.92)
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);[](#l6.93)
/* retry poll() with the recomputed timeout */[](#l6.94)
}[](#l6.95)
- } while (1);
self->poll_running = 0; if (poll_result < 0) {
PyErr_SetFromErrno(PyExc_OSError);[](#l6.101)
if (!async_err)[](#l6.102)
} @@ -577,41 +611,40 @@ poll_poll(pollObject *self, PyObject *ar result_list = PyList_New(poll_result); if (!result_list) return NULL;PyErr_SetFromErrno(PyExc_OSError);[](#l6.103) return NULL;[](#l6.104)
- else {
for (i = 0, j = 0; j < poll_result; j++) {[](#l6.112)
/* skip to the next fired descriptor */[](#l6.113)
while (!self->ufds[i].revents) {[](#l6.114)
i++;[](#l6.115)
}[](#l6.116)
/* if we hit a NULL return, set value to NULL[](#l6.117)
and break out of loop; code at end will[](#l6.118)
clean up result_list */[](#l6.119)
value = PyTuple_New(2);[](#l6.120)
if (value == NULL)[](#l6.121)
goto error;[](#l6.122)
num = PyLong_FromLong(self->ufds[i].fd);[](#l6.123)
if (num == NULL) {[](#l6.124)
Py_DECREF(value);[](#l6.125)
goto error;[](#l6.126)
}[](#l6.127)
PyTuple_SET_ITEM(value, 0, num);[](#l6.128)
/* The &0xffff is a workaround for AIX. 'revents'[](#l6.130)
is a 16-bit short, and IBM assigned POLLNVAL[](#l6.131)
to be 0x8000, so the conversion to int results[](#l6.132)
in a negative number. See SF bug #923315. */[](#l6.133)
num = PyLong_FromLong(self->ufds[i].revents & 0xffff);[](#l6.134)
if (num == NULL) {[](#l6.135)
Py_DECREF(value);[](#l6.136)
goto error;[](#l6.137)
}[](#l6.138)
PyTuple_SET_ITEM(value, 1, num);[](#l6.139)
if ((PyList_SetItem(result_list, j, value)) == -1) {[](#l6.140)
Py_DECREF(value);[](#l6.141)
goto error;[](#l6.142)
}[](#l6.143)
- for (i = 0, j = 0; j < poll_result; j++) {
/* skip to the next fired descriptor */[](#l6.145)
while (!self->ufds[i].revents) {[](#l6.146) i++;[](#l6.147) }[](#l6.148)
/* if we hit a NULL return, set value to NULL[](#l6.149)
and break out of loop; code at end will[](#l6.150)
clean up result_list */[](#l6.151)
value = PyTuple_New(2);[](#l6.152)
if (value == NULL)[](#l6.153)
goto error;[](#l6.154)
num = PyLong_FromLong(self->ufds[i].fd);[](#l6.155)
if (num == NULL) {[](#l6.156)
Py_DECREF(value);[](#l6.157)
goto error;[](#l6.158)
}[](#l6.159)
PyTuple_SET_ITEM(value, 0, num);[](#l6.160)
/* The &0xffff is a workaround for AIX. 'revents'[](#l6.162)
is a 16-bit short, and IBM assigned POLLNVAL[](#l6.163)
to be 0x8000, so the conversion to int results[](#l6.164)
in a negative number. See SF bug #923315. */[](#l6.165)
num = PyLong_FromLong(self->ufds[i].revents & 0xffff);[](#l6.166)
if (num == NULL) {[](#l6.167)
Py_DECREF(value);[](#l6.168)
goto error;[](#l6.169)
}[](#l6.170)
PyTuple_SET_ITEM(value, 1, num);[](#l6.171)
if ((PyList_SetItem(result_list, j, value)) == -1) {[](#l6.172)
Py_DECREF(value);[](#l6.173)
goto error;[](#l6.174)
}[](#l6.175)
} return result_list;i++;[](#l6.176)