_PyTime_ROUND_CEILING=1,
/* Round to nearest with ties going to nearest even integer.
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;
+
/* Convert a time_t to a PyLong. */
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
time_t sec);
os.write(w, b'spam')
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():
run_unittest(PollTests)
tvp = (struct timeval *)NULL;
else {
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
- _PyTime_ROUND_CEILING) < 0) {
+ _PyTime_ROUND_TIMEOUT) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be a float or None");
return NULL;
}
- if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_CEILING) == -1)
+ if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) == -1)
return NULL;
if (tv.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
}
else {
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
- _PyTime_ROUND_CEILING) < 0) {
+ _PyTime_ROUND_TIMEOUT) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be an integer or None");
return NULL;
}
- ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
if (ms < INT_MIN || ms > INT_MAX) {
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
return NULL;
}
else {
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
- _PyTime_ROUND_CEILING) < 0) {
+ _PyTime_ROUND_TIMEOUT) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be an integer or None");
return NULL;
}
- ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
if (ms < -1 || ms > INT_MAX) {
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
return NULL;
/* epoll_wait() has a resolution of 1 millisecond, round towards
infinity to wait at least timeout seconds. */
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
- _PyTime_ROUND_CEILING) < 0) {
+ _PyTime_ROUND_TIMEOUT) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be an integer or None");
}
else {
if (_PyTime_FromSecondsObject(&timeout,
- otimeout, _PyTime_ROUND_CEILING) < 0) {
+ otimeout, _PyTime_ROUND_TIMEOUT) < 0) {
PyErr_Format(PyExc_TypeError,
"timeout argument must be a number "
"or None, got %.200s",