mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
bpo-31786: Make functions in the select module blocking when timeout is a small negative value. (#4003)
This commit is contained in:
parent
552be9d7e6
commit
2c15b29aea
7 changed files with 65 additions and 12 deletions
|
@ -29,9 +29,20 @@ typedef enum {
|
||||||
_PyTime_ROUND_CEILING=1,
|
_PyTime_ROUND_CEILING=1,
|
||||||
/* Round to nearest with ties going to nearest even integer.
|
/* Round to nearest with ties going to nearest even integer.
|
||||||
For example, used to round from a Python float. */
|
For example, used to round from a Python float. */
|
||||||
_PyTime_ROUND_HALF_EVEN
|
_PyTime_ROUND_HALF_EVEN=2,
|
||||||
|
/* Round away from zero
|
||||||
|
For example, used for timeout. _PyTime_ROUND_CEILING rounds
|
||||||
|
-1e-9 to 0 milliseconds which causes bpo-31786 issue.
|
||||||
|
_PyTime_ROUND_UP rounds -1e-9 to -1 millisecond which keeps
|
||||||
|
the timeout sign as expected. select.poll(timeout) must block
|
||||||
|
for negative values." */
|
||||||
|
_PyTime_ROUND_UP=3,
|
||||||
|
/* _PyTime_ROUND_TIMEOUT (an alias for _PyTime_ROUND_UP) should be
|
||||||
|
used for timeouts. */
|
||||||
|
_PyTime_ROUND_TIMEOUT = _PyTime_ROUND_UP
|
||||||
} _PyTime_round_t;
|
} _PyTime_round_t;
|
||||||
|
|
||||||
|
|
||||||
/* Convert a time_t to a PyLong. */
|
/* Convert a time_t to a PyLong. */
|
||||||
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
|
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
|
||||||
time_t sec);
|
time_t sec);
|
||||||
|
|
|
@ -204,6 +204,28 @@ class PollTests(unittest.TestCase):
|
||||||
os.write(w, b'spam')
|
os.write(w, b'spam')
|
||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
|
@unittest.skipUnless(threading, 'Threading required for this test.')
|
||||||
|
@reap_threads
|
||||||
|
def test_poll_blocks_with_negative_ms(self):
|
||||||
|
for timeout_ms in [None, -1, -1.0, -0.1, -1e-100]:
|
||||||
|
# Create two file descriptors. This will be used to unlock
|
||||||
|
# the blocking call to poll.poll inside the thread
|
||||||
|
r, w = os.pipe()
|
||||||
|
pollster = select.poll()
|
||||||
|
pollster.register(r, select.POLLIN)
|
||||||
|
|
||||||
|
poll_thread = threading.Thread(target=pollster.poll, args=(timeout_ms,))
|
||||||
|
poll_thread.start()
|
||||||
|
poll_thread.join(timeout=0.1)
|
||||||
|
self.assertTrue(poll_thread.is_alive())
|
||||||
|
|
||||||
|
# Write to the pipe so pollster.poll unblocks and the thread ends.
|
||||||
|
os.write(w, b'spam')
|
||||||
|
poll_thread.join()
|
||||||
|
self.assertFalse(poll_thread.is_alive())
|
||||||
|
os.close(r)
|
||||||
|
os.close(w)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
run_unittest(PollTests)
|
run_unittest(PollTests)
|
||||||
|
|
|
@ -33,6 +33,8 @@ class _PyTime(enum.IntEnum):
|
||||||
ROUND_CEILING = 1
|
ROUND_CEILING = 1
|
||||||
# Round to nearest with ties going to nearest even integer
|
# Round to nearest with ties going to nearest even integer
|
||||||
ROUND_HALF_EVEN = 2
|
ROUND_HALF_EVEN = 2
|
||||||
|
# Round away from zero
|
||||||
|
ROUND_UP = 3
|
||||||
|
|
||||||
# Rounding modes supported by PyTime
|
# Rounding modes supported by PyTime
|
||||||
ROUNDING_MODES = (
|
ROUNDING_MODES = (
|
||||||
|
@ -40,6 +42,7 @@ ROUNDING_MODES = (
|
||||||
(_PyTime.ROUND_FLOOR, decimal.ROUND_FLOOR),
|
(_PyTime.ROUND_FLOOR, decimal.ROUND_FLOOR),
|
||||||
(_PyTime.ROUND_CEILING, decimal.ROUND_CEILING),
|
(_PyTime.ROUND_CEILING, decimal.ROUND_CEILING),
|
||||||
(_PyTime.ROUND_HALF_EVEN, decimal.ROUND_HALF_EVEN),
|
(_PyTime.ROUND_HALF_EVEN, decimal.ROUND_HALF_EVEN),
|
||||||
|
(_PyTime.ROUND_UP, decimal.ROUND_UP),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix timeout rounding in the select module to round correctly negative timeouts between -1.0 and 0.0.
|
||||||
|
The functions now block waiting for events as expected. Previously, the call was incorrectly non-blocking.
|
||||||
|
Patch by Pablo Galindo.
|
|
@ -3012,7 +3012,8 @@ check_time_rounding(int round)
|
||||||
{
|
{
|
||||||
if (round != _PyTime_ROUND_FLOOR
|
if (round != _PyTime_ROUND_FLOOR
|
||||||
&& round != _PyTime_ROUND_CEILING
|
&& round != _PyTime_ROUND_CEILING
|
||||||
&& round != _PyTime_ROUND_HALF_EVEN) {
|
&& round != _PyTime_ROUND_HALF_EVEN
|
||||||
|
&& round != _PyTime_ROUND_UP) {
|
||||||
PyErr_SetString(PyExc_ValueError, "invalid rounding");
|
PyErr_SetString(PyExc_ValueError, "invalid rounding");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,7 +213,7 @@ select_select(PyObject *self, PyObject *args)
|
||||||
tvp = (struct timeval *)NULL;
|
tvp = (struct timeval *)NULL;
|
||||||
else {
|
else {
|
||||||
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
||||||
_PyTime_ROUND_CEILING) < 0) {
|
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"timeout must be a float or None");
|
"timeout must be a float or None");
|
||||||
|
@ -221,7 +221,7 @@ select_select(PyObject *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_CEILING) == -1)
|
if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (tv.tv_sec < 0) {
|
if (tv.tv_sec < 0) {
|
||||||
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
|
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
|
||||||
|
@ -540,7 +540,7 @@ poll_poll(pollObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
||||||
_PyTime_ROUND_CEILING) < 0) {
|
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"timeout must be an integer or None");
|
"timeout must be an integer or None");
|
||||||
|
@ -548,7 +548,7 @@ poll_poll(pollObject *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
|
||||||
if (ms < INT_MIN || ms > INT_MAX) {
|
if (ms < INT_MIN || ms > INT_MAX) {
|
||||||
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -896,7 +896,7 @@ devpoll_poll(devpollObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
||||||
_PyTime_ROUND_CEILING) < 0) {
|
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"timeout must be an integer or None");
|
"timeout must be an integer or None");
|
||||||
|
@ -904,7 +904,7 @@ devpoll_poll(devpollObject *self, PyObject *args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
|
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
|
||||||
if (ms < -1 || ms > INT_MAX) {
|
if (ms < -1 || ms > INT_MAX) {
|
||||||
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1513,7 +1513,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
|
||||||
/* epoll_wait() has a resolution of 1 millisecond, round towards
|
/* epoll_wait() has a resolution of 1 millisecond, round towards
|
||||||
infinity to wait at least timeout seconds. */
|
infinity to wait at least timeout seconds. */
|
||||||
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
||||||
_PyTime_ROUND_CEILING) < 0) {
|
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"timeout must be an integer or None");
|
"timeout must be an integer or None");
|
||||||
|
@ -2128,7 +2128,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_PyTime_FromSecondsObject(&timeout,
|
if (_PyTime_FromSecondsObject(&timeout,
|
||||||
otimeout, _PyTime_ROUND_CEILING) < 0) {
|
otimeout, _PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"timeout argument must be a number "
|
"timeout argument must be a number "
|
||||||
"or None, got %.200s",
|
"or None, got %.200s",
|
||||||
|
|
|
@ -120,9 +120,13 @@ _PyTime_Round(double x, _PyTime_round_t round)
|
||||||
else if (round == _PyTime_ROUND_CEILING) {
|
else if (round == _PyTime_ROUND_CEILING) {
|
||||||
d = ceil(d);
|
d = ceil(d);
|
||||||
}
|
}
|
||||||
else {
|
else if (round == _PyTime_ROUND_FLOOR) {
|
||||||
d = floor(d);
|
d = floor(d);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
assert(round == _PyTime_ROUND_UP);
|
||||||
|
d = (d >= 0.0) ? ceil(d) : floor(d);
|
||||||
|
}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,7 +431,7 @@ _PyTime_Divide(const _PyTime_t t, const _PyTime_t k,
|
||||||
return t / k;
|
return t / k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (round == _PyTime_ROUND_FLOOR){
|
||||||
if (t >= 0) {
|
if (t >= 0) {
|
||||||
return t / k;
|
return t / k;
|
||||||
}
|
}
|
||||||
|
@ -435,6 +439,15 @@ _PyTime_Divide(const _PyTime_t t, const _PyTime_t k,
|
||||||
return (t - (k - 1)) / k;
|
return (t - (k - 1)) / k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
assert(round == _PyTime_ROUND_UP);
|
||||||
|
if (t >= 0) {
|
||||||
|
return (t + k - 1) / k;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (t - (k - 1)) / k;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_PyTime_t
|
_PyTime_t
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue