| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -29,9 +29,20 @@ typedef enum { | |||
| 29 | 29 | _PyTime_ROUND_CEILING=1, | |
| 30 | 30 | /* Round to nearest with ties going to nearest even integer. | |
| 31 | 31 | For example, used to round from a Python float. */ | |
| 32 | - _PyTime_ROUND_HALF_EVEN | ||
| 32 | + _PyTime_ROUND_HALF_EVEN=2, | ||
| 33 | + /* Round away from zero | ||
| 34 | + For example, used for timeout. _PyTime_ROUND_CEILING rounds | ||
| 35 | + -1e-9 to 0 milliseconds which causes bpo-31786 issue. | ||
| 36 | + _PyTime_ROUND_UP rounds -1e-9 to -1 millisecond which keeps | ||
| 37 | + the timeout sign as expected. select.poll(timeout) must block | ||
| 38 | + for negative values." */ | ||
| 39 | + _PyTime_ROUND_UP=3, | ||
| 40 | + /* _PyTime_ROUND_TIMEOUT (an alias for _PyTime_ROUND_UP) should be | ||
| 41 | + used for timeouts. */ | ||
| 42 | + _PyTime_ROUND_TIMEOUT = _PyTime_ROUND_UP | ||
| 33 | 43 | } _PyTime_round_t; | |
| 34 | 44 | ||
| 45 | + | ||
| 35 | 46 | /* Convert a time_t to a PyLong. */ | |
| 36 | 47 | PyAPI_FUNC(PyObject *) _PyLong_FromTime_t( | |
| 37 | 48 | time_t sec); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -204,6 +204,28 @@ def test_threaded_poll(self): | |||
| 204 | 204 | os.write(w, b'spam') | |
| 205 | 205 | t.join() | |
| 206 | 206 | ||
| 207 | + @unittest.skipUnless(threading, 'Threading required for this test.') | ||
| 208 | + @reap_threads | ||
| 209 | + def test_poll_blocks_with_negative_ms(self): | ||
| 210 | + for timeout_ms in [None, -1, -1.0, -0.1, -1e-100]: | ||
| 211 | + # Create two file descriptors. This will be used to unlock | ||
| 212 | + # the blocking call to poll.poll inside the thread | ||
| 213 | + r, w = os.pipe() | ||
| 214 | + pollster = select.poll() | ||
| 215 | + pollster.register(r, select.POLLIN) | ||
| 216 | + | ||
| 217 | + poll_thread = threading.Thread(target=pollster.poll, args=(timeout_ms,)) | ||
| 218 | + poll_thread.start() | ||
| 219 | + poll_thread.join(timeout=0.1) | ||
| 220 | + self.assertTrue(poll_thread.is_alive()) | ||
| 221 | + | ||
| 222 | + # Write to the pipe so pollster.poll unblocks and the thread ends. | ||
| 223 | + os.write(w, b'spam') | ||
| 224 | + poll_thread.join() | ||
| 225 | + self.assertFalse(poll_thread.is_alive()) | ||
| 226 | + os.close(r) | ||
| 227 | + os.close(w) | ||
| 228 | + | ||
| 207 | 229 | ||
| 208 | 230 | def test_main(): | |
| 209 | 231 | run_unittest(PollTests) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,13 +33,16 @@ class _PyTime(enum.IntEnum): | |||
| 33 | 33 | ROUND_CEILING = 1 | |
| 34 | 34 | # Round to nearest with ties going to nearest even integer | |
| 35 | 35 | ROUND_HALF_EVEN = 2 | |
| 36 | + # Round away from zero | ||
| 37 | + ROUND_UP = 3 | ||
| 36 | 38 | ||
| 37 | 39 | # Rounding modes supported by PyTime | |
| 38 | 40 | ROUNDING_MODES = ( | |
| 39 | 41 | # (PyTime rounding method, decimal rounding method) | |
| 40 | 42 | (_PyTime.ROUND_FLOOR, decimal.ROUND_FLOOR), | |
| 41 | 43 | (_PyTime.ROUND_CEILING, decimal.ROUND_CEILING), | |
| 42 | 44 | (_PyTime.ROUND_HALF_EVEN, decimal.ROUND_HALF_EVEN), | |
| 45 | + (_PyTime.ROUND_UP, decimal.ROUND_UP), | ||
| 43 | 46 | ) | |
| 44 | 47 | ||
| 45 | 48 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Fix timeout rounding in the select module to round correctly negative timeouts between -1.0 and 0.0. | ||
| 2 | + The functions now block waiting for events as expected. Previously, the call was incorrectly non-blocking. | ||
| 3 | + Patch by Pablo Galindo. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3012,7 +3012,8 @@ check_time_rounding(int round) | |||
| 3012 | 3012 | { | |
| 3013 | 3013 | if (round != _PyTime_ROUND_FLOOR | |
| 3014 | 3014 | && round != _PyTime_ROUND_CEILING | |
| 3015 | - && round != _PyTime_ROUND_HALF_EVEN) { | ||
| 3015 | + && round != _PyTime_ROUND_HALF_EVEN | ||
| 3016 | + && round != _PyTime_ROUND_UP) { | ||
| 3016 | 3017 | PyErr_SetString(PyExc_ValueError, "invalid rounding"); | |
| 3017 | 3018 | return -1; | |
| 3018 | 3019 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -213,15 +213,15 @@ select_select(PyObject *self, PyObject *args) | |||
| 213 | 213 | tvp = (struct timeval *)NULL; | |
| 214 | 214 | else { | |
| 215 | 215 | if (_PyTime_FromSecondsObject(&timeout, timeout_obj, | |
| 216 | - _PyTime_ROUND_CEILING) < 0) { | ||
| 216 | + _PyTime_ROUND_TIMEOUT) < 0) { | ||
| 217 | 217 | if (PyErr_ExceptionMatches(PyExc_TypeError)) { | |
| 218 | 218 | PyErr_SetString(PyExc_TypeError, | |
| 219 | 219 | "timeout must be a float or None"); | |
| 220 | 220 | } | |
| 221 | 221 | return NULL; | |
| 222 | 222 | } | |
| 223 | 223 | ||
| 224 | - if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_CEILING) == -1) | ||
| 224 | + if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) == -1) | ||
| 225 | 225 | return NULL; | |
| 226 | 226 | if (tv.tv_sec < 0) { | |
| 227 | 227 | PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); | |
@@ -540,15 +540,15 @@ poll_poll(pollObject *self, PyObject *args) | |||
| 540 | 540 | } | |
| 541 | 541 | else { | |
| 542 | 542 | if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, | |
| 543 | - _PyTime_ROUND_CEILING) < 0) { | ||
| 543 | + _PyTime_ROUND_TIMEOUT) < 0) { | ||
| 544 | 544 | if (PyErr_ExceptionMatches(PyExc_TypeError)) { | |
| 545 | 545 | PyErr_SetString(PyExc_TypeError, | |
| 546 | 546 | "timeout must be an integer or None"); | |
| 547 | 547 | } | |
| 548 | 548 | return NULL; | |
| 549 | 549 | } | |
| 550 | 550 | ||
| 551 | - ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); | ||
| 551 | + ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); | ||
| 552 | 552 | if (ms < INT_MIN || ms > INT_MAX) { | |
| 553 | 553 | PyErr_SetString(PyExc_OverflowError, "timeout is too large"); | |
| 554 | 554 | return NULL; | |
@@ -896,15 +896,15 @@ devpoll_poll(devpollObject *self, PyObject *args) | |||
| 896 | 896 | } | |
| 897 | 897 | else { | |
| 898 | 898 | if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, | |
| 899 | - _PyTime_ROUND_CEILING) < 0) { | ||
| 899 | + _PyTime_ROUND_TIMEOUT) < 0) { | ||
| 900 | 900 | if (PyErr_ExceptionMatches(PyExc_TypeError)) { | |
| 901 | 901 | PyErr_SetString(PyExc_TypeError, | |
| 902 | 902 | "timeout must be an integer or None"); | |
| 903 | 903 | } | |
| 904 | 904 | return NULL; | |
| 905 | 905 | } | |
| 906 | 906 | ||
| 907 | - ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); | ||
| 907 | + ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); | ||
| 908 | 908 | if (ms < -1 || ms > INT_MAX) { | |
| 909 | 909 | PyErr_SetString(PyExc_OverflowError, "timeout is too large"); | |
| 910 | 910 | return NULL; | |
@@ -1513,7 +1513,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) | |||
| 1513 | 1513 | /* epoll_wait() has a resolution of 1 millisecond, round towards | |
| 1514 | 1514 | infinity to wait at least timeout seconds. */ | |
| 1515 | 1515 | if (_PyTime_FromSecondsObject(&timeout, timeout_obj, | |
| 1516 | - _PyTime_ROUND_CEILING) < 0) { | ||
| 1516 | + _PyTime_ROUND_TIMEOUT) < 0) { | ||
| 1517 | 1517 | if (PyErr_ExceptionMatches(PyExc_TypeError)) { | |
| 1518 | 1518 | PyErr_SetString(PyExc_TypeError, | |
| 1519 | 1519 | "timeout must be an integer or None"); | |
@@ -2128,7 +2128,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) | |||
| 2128 | 2128 | } | |
| 2129 | 2129 | else { | |
| 2130 | 2130 | if (_PyTime_FromSecondsObject(&timeout, | |
| 2131 | - otimeout, _PyTime_ROUND_CEILING) < 0) { | ||
| 2131 | + otimeout, _PyTime_ROUND_TIMEOUT) < 0) { | ||
| 2132 | 2132 | PyErr_Format(PyExc_TypeError, | |
| 2133 | 2133 | "timeout argument must be a number " | |
| 2134 | 2134 | "or None, got %.200s", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -120,9 +120,13 @@ _PyTime_Round(double x, _PyTime_round_t round) | |||
| 120 | 120 | else if (round == _PyTime_ROUND_CEILING) { | |
| 121 | 121 | d = ceil(d); | |
| 122 | 122 | } | |
| 123 | - else { | ||
| 123 | + else if (round == _PyTime_ROUND_FLOOR) { | ||
| 124 | 124 | d = floor(d); | |
| 125 | 125 | } | |
| 126 | + else { | ||
| 127 | + assert(round == _PyTime_ROUND_UP); | ||
| 128 | + d = (d >= 0.0) ? ceil(d) : floor(d); | ||
| 129 | + } | ||
| 126 | 130 | return d; | |
| 127 | 131 | } | |
| 128 | 132 | ||
@@ -427,14 +431,23 @@ _PyTime_Divide(const _PyTime_t t, const _PyTime_t k, | |||
| 427 | 431 | return t / k; | |
| 428 | 432 | } | |
| 429 | 433 | } | |
| 430 | - else { | ||
| 434 | + else if (round == _PyTime_ROUND_FLOOR){ | ||
| 431 | 435 | if (t >= 0) { | |
| 432 | 436 | return t / k; | |
| 433 | 437 | } | |
| 434 | 438 | else { | |
| 435 | 439 | return (t - (k - 1)) / k; | |
| 436 | 440 | } | |
| 437 | 441 | } | |
| 442 | + else { | ||
| 443 | + assert(round == _PyTime_ROUND_UP); | ||
| 444 | + if (t >= 0) { | ||
| 445 | + return (t + k - 1) / k; | ||
| 446 | + } | ||
| 447 | + else { | ||
| 448 | + return (t - (k - 1)) / k; | ||
| 449 | + } | ||
| 450 | + } | ||
| 438 | 451 | } | |
| 439 | 452 | ||
| 440 | 453 | _PyTime_t | |
| Back | FazBrowse Home | New Git URL |
0 commit comments