[Python-checkins] bpo-41710: Add private _PyDeadline_Get() function (GH-28674)

vstinner webhook-mailer at python.org
Fri Oct 1 07:29:30 EDT 2021


https://github.com/python/cpython/commit/833fdf126c8fe77fd17e8a8ffbc5c571b3bf64bd
commit: 833fdf126c8fe77fd17e8a8ffbc5c571b3bf64bd
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-10-01T13:29:25+02:00
summary:

bpo-41710: Add private _PyDeadline_Get() function (GH-28674)

Add a private C API for deadlines: add _PyDeadline_Init() and
_PyDeadline_Get() functions.

* Add _PyTime_Add() and _PyTime_Mul() functions which compute t1+t2
  and t1*t2 and clamp the result on overflow.
* _PyTime_MulDiv() now uses _PyTime_Add() and _PyTime_Mul().

files:
M Include/cpython/pytime.h
M Modules/_queuemodule.c
M Modules/_ssl.c
M Modules/_threadmodule.c
M Modules/clinic/_queuemodule.c.h
M Modules/selectmodule.c
M Modules/signalmodule.c
M Modules/socketmodule.c
M Python/pytime.c
M Python/thread_nt.h
M Python/thread_pthread.h

diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h
index 5440720f1ba71..f32148aa8448d 100644
--- a/Include/cpython/pytime.h
+++ b/Include/cpython/pytime.h
@@ -27,6 +27,8 @@
 //
 // Some functions clamp the result in the range [_PyTime_MIN; _PyTime_MAX], so
 // the caller doesn't have to handle errors and doesn't need to hold the GIL.
+// For example, _PyTime_Add(t1, t2) computes t1+t2 and clamp the result on
+// overflow.
 //
 // Clocks:
 //
@@ -215,7 +217,12 @@ PyAPI_FUNC(int) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts);
 PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
 #endif
 
+
+// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+PyAPI_FUNC(_PyTime_t) _PyTime_Add(_PyTime_t t1, _PyTime_t t2);
+
 /* Compute ticks * mul / div.
+   Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
    The caller must ensure that ((div - 1) * mul) cannot overflow. */
 PyAPI_FUNC(_PyTime_t) _PyTime_MulDiv(_PyTime_t ticks,
     _PyTime_t mul,
@@ -299,6 +306,15 @@ PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo(
     _PyTime_t *t,
     _Py_clock_info_t *info);
 
+
+// Create a deadline.
+// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
+PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout);
+
+// Get remaining time from a deadline.
+// Pseudo code: deadline - _PyTime_GetMonotonicClock().
+PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c
index b6769e6b7764e..eb61349b76581 100644
--- a/Modules/_queuemodule.c
+++ b/Modules/_queuemodule.c
@@ -182,7 +182,7 @@ _queue.SimpleQueue.get
     cls: defining_class
     /
     block: bool = True
-    timeout: object = None
+    timeout as timeout_obj: object = None
 
 Remove and return an item from the queue.
 
@@ -198,11 +198,11 @@ in that case).
 
 static PyObject *
 _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
-                            int block, PyObject *timeout)
-/*[clinic end generated code: output=1969aefa7db63666 input=5fc4d56b9a54757e]*/
+                            int block, PyObject *timeout_obj)
+/*[clinic end generated code: output=5c2cca914cd1e55b input=5b4047bfbc645ec1]*/
 {
     _PyTime_t endtime = 0;
-    _PyTime_t timeout_val;
+    _PyTime_t timeout;
     PyObject *item;
     PyLockStatus r;
     PY_TIMEOUT_T microseconds;
@@ -211,24 +211,25 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
         /* Non-blocking */
         microseconds = 0;
     }
-    else if (timeout != Py_None) {
+    else if (timeout_obj != Py_None) {
         /* With timeout */
-        if (_PyTime_FromSecondsObject(&timeout_val,
-                                      timeout, _PyTime_ROUND_CEILING) < 0)
+        if (_PyTime_FromSecondsObject(&timeout,
+                                      timeout_obj, _PyTime_ROUND_CEILING) < 0) {
             return NULL;
-        if (timeout_val < 0) {
+        }
+        if (timeout < 0) {
             PyErr_SetString(PyExc_ValueError,
                             "'timeout' must be a non-negative number");
             return NULL;
         }
-        microseconds = _PyTime_AsMicroseconds(timeout_val,
+        microseconds = _PyTime_AsMicroseconds(timeout,
                                               _PyTime_ROUND_CEILING);
         if (microseconds > PY_TIMEOUT_MAX) {
             PyErr_SetString(PyExc_OverflowError,
                             "timeout value is too large");
             return NULL;
         }
-        endtime = _PyTime_GetMonotonicClock() + timeout_val;
+        endtime = _PyDeadline_Init(timeout);
     }
     else {
         /* Infinitely blocking */
@@ -247,6 +248,7 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
             r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
             Py_END_ALLOW_THREADS
         }
+
         if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
             return NULL;
         }
@@ -258,12 +260,15 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
             return NULL;
         }
         self->locked = 1;
+
         /* Adjust timeout for next iteration (if any) */
-        if (endtime > 0) {
-            timeout_val = endtime - _PyTime_GetMonotonicClock();
-            microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING);
+        if (microseconds > 0) {
+            timeout = _PyDeadline_Get(endtime);
+            microseconds = _PyTime_AsMicroseconds(timeout,
+                                                  _PyTime_ROUND_CEILING);
         }
     }
+
     /* BEGIN GIL-protected critical section */
     assert(self->lst_pos < PyList_GET_SIZE(self->lst));
     item = simplequeue_pop_item(self);
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 411314f6d0f01..fedb35b5778b3 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -949,8 +949,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
 
     timeout = GET_SOCKET_TIMEOUT(sock);
     has_timeout = (timeout > 0);
-    if (has_timeout)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (has_timeout) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     /* Actually negotiate SSL connection */
     /* XXX If SSL_do_handshake() returns 0, it's also a failure. */
@@ -965,7 +966,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
             goto error;
 
         if (has_timeout)
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
 
         if (err.ssl == SSL_ERROR_WANT_READ) {
             sockstate = PySSL_select(sock, 0, timeout);
@@ -2326,8 +2327,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
 
     timeout = GET_SOCKET_TIMEOUT(sock);
     has_timeout = (timeout > 0);
-    if (has_timeout)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (has_timeout) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     sockstate = PySSL_select(sock, 1, timeout);
     if (sockstate == SOCKET_HAS_TIMED_OUT) {
@@ -2354,8 +2356,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
         if (PyErr_CheckSignals())
             goto error;
 
-        if (has_timeout)
-            timeout = deadline - _PyTime_GetMonotonicClock();
+        if (has_timeout) {
+            timeout = _PyDeadline_Get(deadline);
+        }
 
         if (err.ssl == SSL_ERROR_WANT_READ) {
             sockstate = PySSL_select(sock, 0, timeout);
@@ -2494,7 +2497,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
     timeout = GET_SOCKET_TIMEOUT(sock);
     has_timeout = (timeout > 0);
     if (has_timeout)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+        deadline = _PyDeadline_Init(timeout);
 
     do {
         PySSL_BEGIN_ALLOW_THREADS
@@ -2506,8 +2509,9 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
         if (PyErr_CheckSignals())
             goto error;
 
-        if (has_timeout)
-            timeout = deadline - _PyTime_GetMonotonicClock();
+        if (has_timeout) {
+            timeout = _PyDeadline_Get(deadline);
+        }
 
         if (err.ssl == SSL_ERROR_WANT_READ) {
             sockstate = PySSL_select(sock, 0, timeout);
@@ -2592,8 +2596,9 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
 
     timeout = GET_SOCKET_TIMEOUT(sock);
     has_timeout = (timeout > 0);
-    if (has_timeout)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (has_timeout) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     while (1) {
         PySSL_BEGIN_ALLOW_THREADS
@@ -2626,8 +2631,9 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
             continue;
         }
 
-        if (has_timeout)
-            timeout = deadline - _PyTime_GetMonotonicClock();
+        if (has_timeout) {
+            timeout = _PyDeadline_Get(deadline);
+        }
 
         /* Possibly retry shutdown until timeout or failure */
         if (err.ssl == SSL_ERROR_WANT_READ)
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index fa7e6d0e09d18..543d82d0b9382 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -84,13 +84,12 @@ lock_dealloc(lockobject *self)
 static PyLockStatus
 acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
 {
-    PyLockStatus r;
     _PyTime_t endtime = 0;
-
     if (timeout > 0) {
-        endtime = _PyTime_GetMonotonicClock() + timeout;
+        endtime = _PyDeadline_Init(timeout);
     }
 
+    PyLockStatus r;
     do {
         _PyTime_t microseconds;
         microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
@@ -114,7 +113,7 @@ acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
             /* If we're using a timeout, recompute the timeout after processing
              * signals, since those can take time.  */
             if (timeout > 0) {
-                timeout = endtime - _PyTime_GetMonotonicClock();
+                timeout = _PyDeadline_Get(endtime);
 
                 /* Check for negative values, since those mean block forever.
                  */
diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h
index d9522562359e6..22d2e992b6398 100644
--- a/Modules/clinic/_queuemodule.c.h
+++ b/Modules/clinic/_queuemodule.c.h
@@ -139,7 +139,7 @@ PyDoc_STRVAR(_queue_SimpleQueue_get__doc__,
 
 static PyObject *
 _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
-                            int block, PyObject *timeout);
+                            int block, PyObject *timeout_obj);
 
 static PyObject *
 _queue_SimpleQueue_get(simplequeueobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@@ -148,13 +148,13 @@ _queue_SimpleQueue_get(simplequeueobject *self, PyTypeObject *cls, PyObject *con
     static const char * const _keywords[] = {"block", "timeout", NULL};
     static _PyArg_Parser _parser = {"|pO:get", _keywords, 0};
     int block = 1;
-    PyObject *timeout = Py_None;
+    PyObject *timeout_obj = Py_None;
 
     if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &block, &timeout)) {
+        &block, &timeout_obj)) {
         goto exit;
     }
-    return_value = _queue_SimpleQueue_get_impl(self, cls, block, timeout);
+    return_value = _queue_SimpleQueue_get_impl(self, cls, block, timeout_obj);
 
 exit:
     return return_value;
@@ -248,4 +248,4 @@ _queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored))
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=96cc57168d72aab1 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=acfaf0191d8935db input=a9049054013a1b77]*/
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index b71b2c4f6c037..ff1c028d0d672 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -318,8 +318,9 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
     if (omax > max) max = omax;
     if (emax > max) max = emax;
 
-    if (tvp)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (tvp) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     do {
         Py_BEGIN_ALLOW_THREADS
@@ -335,7 +336,7 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
             goto finally;
 
         if (tvp) {
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 /* bpo-35310: lists were unmodified -- clear them explicitly */
                 FD_ZERO(&ifdset);
@@ -599,7 +600,7 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
         }
 
         if (timeout >= 0) {
-            deadline = _PyTime_GetMonotonicClock() + timeout;
+            deadline = _PyDeadline_Init(timeout);
         }
     }
 
@@ -646,7 +647,7 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
         }
 
         if (timeout >= 0) {
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 poll_result = 0;
                 break;
@@ -938,8 +939,9 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
     dvp.dp_nfds = self->max_n_fds;
     dvp.dp_timeout = (int)ms;
 
-    if (timeout >= 0)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (timeout >= 0) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     do {
         /* call devpoll() */
@@ -956,7 +958,7 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
             return NULL;
 
         if (timeout >= 0) {
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 poll_result = 0;
                 break;
@@ -1550,7 +1552,7 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
         }
 
         if (timeout >= 0) {
-            deadline = _PyTime_GetMonotonicClock() + timeout;
+            deadline = _PyDeadline_Init(timeout);
         }
     }
 
@@ -1584,7 +1586,7 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
             goto error;
 
         if (timeout >= 0) {
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 nfds = 0;
                 break;
@@ -2172,8 +2174,9 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
         }
     }
 
-    if (ptimeoutspec)
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (ptimeoutspec) {
+        deadline = _PyDeadline_Init(timeout);
+    }
 
     do {
         Py_BEGIN_ALLOW_THREADS
@@ -2190,7 +2193,7 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
             goto error;
 
         if (ptimeoutspec) {
-            timeout = deadline - _PyTime_GetMonotonicClock();
+            timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 gotevents = 0;
                 break;
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index fc58cfd2084c7..8b7ef2cc688fe 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -1221,11 +1221,7 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
                          PyObject *timeout_obj)
 /*[clinic end generated code: output=59c8971e8ae18a64 input=87fd39237cf0b7ba]*/
 {
-    struct timespec ts;
-    siginfo_t si;
-    int res;
-    _PyTime_t timeout, deadline, monotonic;
-
+    _PyTime_t timeout;
     if (_PyTime_FromSecondsObject(&timeout,
                                   timeout_obj, _PyTime_ROUND_CEILING) < 0)
         return NULL;
@@ -1235,12 +1231,16 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
         return NULL;
     }
 
-    deadline = _PyTime_GetMonotonicClock() + timeout;
+    _PyTime_t deadline = _PyDeadline_Init(timeout);
+    siginfo_t si;
 
     do {
-        if (_PyTime_AsTimespec(timeout, &ts) < 0)
+        struct timespec ts;
+        if (_PyTime_AsTimespec(timeout, &ts) < 0) {
             return NULL;
+        }
 
+        int res;
         Py_BEGIN_ALLOW_THREADS
         res = sigtimedwait(&sigset, &si, &ts);
         Py_END_ALLOW_THREADS
@@ -1259,10 +1259,10 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
         if (PyErr_CheckSignals())
             return NULL;
 
-        monotonic = _PyTime_GetMonotonicClock();
-        timeout = deadline - monotonic;
-        if (timeout < 0)
+        timeout = _PyDeadline_Get(deadline);
+        if (timeout < 0) {
             break;
+        }
     } while (1);
 
     return fill_siginfo(&si);
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index f474869c94dc2..98212274b46a9 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -840,18 +840,20 @@ sock_call_ex(PySocketSockObject *s,
 
                 if (deadline_initialized) {
                     /* recompute the timeout */
-                    interval = deadline - _PyTime_GetMonotonicClock();
+                    interval = _PyDeadline_Get(deadline);
                 }
                 else {
                     deadline_initialized = 1;
-                    deadline = _PyTime_GetMonotonicClock() + timeout;
+                    deadline = _PyDeadline_Init(timeout);
                     interval = timeout;
                 }
 
-                if (interval >= 0)
+                if (interval >= 0) {
                     res = internal_select(s, writing, interval, connect);
-                else
+                }
+                else {
                     res = 1;
+                }
             }
             else {
                 res = internal_select(s, writing, timeout, connect);
@@ -4176,7 +4178,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
     Py_buffer pbuf;
     struct sock_send ctx;
     int has_timeout = (s->sock_timeout > 0);
-    _PyTime_t interval = s->sock_timeout;
+    _PyTime_t timeout = s->sock_timeout;
     _PyTime_t deadline = 0;
     int deadline_initialized = 0;
     PyObject *res = NULL;
@@ -4195,14 +4197,14 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
         if (has_timeout) {
             if (deadline_initialized) {
                 /* recompute the timeout */
-                interval = deadline - _PyTime_GetMonotonicClock();
+                timeout = _PyDeadline_Get(deadline);
             }
             else {
                 deadline_initialized = 1;
-                deadline = _PyTime_GetMonotonicClock() + s->sock_timeout;
+                deadline = _PyDeadline_Init(timeout);
             }
 
-            if (interval <= 0) {
+            if (timeout <= 0) {
                 PyErr_SetString(PyExc_TimeoutError, "timed out");
                 goto done;
             }
@@ -4211,7 +4213,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
         ctx.buf = buf;
         ctx.len = len;
         ctx.flags = flags;
-        if (sock_call_ex(s, 1, sock_send_impl, &ctx, 0, NULL, interval) < 0)
+        if (sock_call_ex(s, 1, sock_send_impl, &ctx, 0, NULL, timeout) < 0)
             goto done;
         n = ctx.result;
         assert(n >= 0);
diff --git a/Python/pytime.c b/Python/pytime.c
index 7fd03ea576d27..8865638e91c23 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -73,30 +73,43 @@ pytime_as_nanoseconds(_PyTime_t t)
 }
 
 
-// Compute t + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
 static inline int
-pytime_add(_PyTime_t *t, _PyTime_t t2)
+pytime_add(_PyTime_t *t1, _PyTime_t t2)
 {
-    if (t2 > 0 && *t > _PyTime_MAX - t2) {
-        *t = _PyTime_MAX;
+    if (t2 > 0 && *t1 > _PyTime_MAX - t2) {
+        *t1 = _PyTime_MAX;
         return -1;
     }
-    else if (t2 < 0 && *t < _PyTime_MIN - t2) {
-        *t = _PyTime_MIN;
+    else if (t2 < 0 && *t1 < _PyTime_MIN - t2) {
+        *t1 = _PyTime_MIN;
         return -1;
     }
     else {
-        *t += t2;
+        *t1 += t2;
         return 0;
     }
 }
 
 
+_PyTime_t
+_PyTime_Add(_PyTime_t t1, _PyTime_t t2)
+{
+    (void)pytime_add(&t1, t2);
+    return t1;
+}
+
+
 static inline int
-_PyTime_check_mul_overflow(_PyTime_t a, _PyTime_t b)
+pytime_mul_check_overflow(_PyTime_t a, _PyTime_t b)
 {
-    assert(b > 0);
-    return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
+    if (b != 0) {
+        assert(b > 0);
+        return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
+    }
+    else {
+        return 0;
+    }
 }
 
 
@@ -104,8 +117,8 @@ _PyTime_check_mul_overflow(_PyTime_t a, _PyTime_t b)
 static inline int
 pytime_mul(_PyTime_t *t, _PyTime_t k)
 {
-    assert(k > 0);
-    if (_PyTime_check_mul_overflow(*t, k)) {
+    assert(k >= 0);
+    if (pytime_mul_check_overflow(*t, k)) {
         *t = (*t >= 0) ? _PyTime_MAX : _PyTime_MIN;
         return -1;
     }
@@ -116,21 +129,31 @@ pytime_mul(_PyTime_t *t, _PyTime_t k)
 }
 
 
+// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+static inline _PyTime_t
+_PyTime_Mul(_PyTime_t t, _PyTime_t k)
+{
+    (void)pytime_mul(&t, k);
+    return t;
+}
+
+
+
+
 _PyTime_t
 _PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div)
 {
-    _PyTime_t intpart, remaining;
-    /* Compute (ticks * mul / div) in two parts to prevent integer overflow:
-       compute integer part, and then the remaining part.
+    /* Compute (ticks * mul / div) in two parts to reduce the risk of integer
+       overflow: compute the integer part, and then the remaining part.
 
        (ticks * mul) / div == (ticks / div) * mul + (ticks % div) * mul / div
-
-       The caller must ensure that "(div - 1) * mul" cannot overflow. */
+    */
+    _PyTime_t intpart, remaining;
     intpart = ticks / div;
     ticks %= div;
-    remaining = ticks * mul;
-    remaining /= div;
-    return intpart * mul + remaining;
+    remaining = _PyTime_Mul(ticks, mul) / div;
+    // intpart * mul + remaining
+    return _PyTime_Add(_PyTime_Mul(intpart, mul), remaining);
 }
 
 
@@ -505,7 +528,6 @@ pytime_from_object(_PyTime_t *tp, PyObject *obj, _PyTime_round_t round,
         return pytime_from_double(tp, d, round, unit_to_ns);
     }
     else {
-        Py_BUILD_ASSERT(sizeof(long long) <= sizeof(_PyTime_t));
         long long sec = PyLong_AsLongLong(obj);
         if (sec == -1 && PyErr_Occurred()) {
             if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
@@ -514,11 +536,12 @@ pytime_from_object(_PyTime_t *tp, PyObject *obj, _PyTime_round_t round,
             return -1;
         }
 
-        if (_PyTime_check_mul_overflow(sec, unit_to_ns)) {
+        Py_BUILD_ASSERT(sizeof(long long) <= sizeof(_PyTime_t));
+        _PyTime_t ns = (_PyTime_t)sec;
+        if (pytime_mul(&ns, unit_to_ns) < 0) {
             pytime_overflow();
             return -1;
         }
-        _PyTime_t ns = sec * unit_to_ns;
 
         *tp = pytime_from_nanoseconds(ns);
         return 0;
@@ -1292,3 +1315,19 @@ _PyTime_gmtime(time_t t, struct tm *tm)
     return 0;
 #endif /* MS_WINDOWS */
 }
+
+
+_PyTime_t
+_PyDeadline_Init(_PyTime_t timeout)
+{
+    _PyTime_t now = _PyTime_GetMonotonicClock();
+    return _PyTime_Add(now, timeout);
+}
+
+
+_PyTime_t
+_PyDeadline_Get(_PyTime_t deadline)
+{
+    _PyTime_t now = _PyTime_GetMonotonicClock();
+    return deadline - now;
+}
diff --git a/Python/thread_nt.h b/Python/thread_nt.h
index 26f054ff0ce49..0dde1a0409738 100644
--- a/Python/thread_nt.h
+++ b/Python/thread_nt.h
@@ -75,20 +75,20 @@ EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds)
             }
         }
     } else if (milliseconds != 0) {
-        /* wait at least until the target */
-        _PyTime_t now = _PyTime_GetPerfCounter();
+        /* wait at least until the deadline */
         _PyTime_t nanoseconds = _PyTime_FromNanoseconds((_PyTime_t)milliseconds * 1000000);
-        _PyTime_t target = now + nanoseconds;
+        _PyTime_t deadline = _PyTime_Add(_PyTime_GetPerfCounter(), nanoseconds);
         while (mutex->locked) {
-            _PyTime_t microseconds = _PyTime_AsMicroseconds(nanoseconds, _PyTime_ROUND_TIMEOUT);
+            _PyTime_t microseconds = _PyTime_AsMicroseconds(nanoseconds,
+                                                            _PyTime_ROUND_TIMEOUT);
             if (PyCOND_TIMEDWAIT(&mutex->cv, &mutex->cs, microseconds) < 0) {
                 result = WAIT_FAILED;
                 break;
             }
-            now = _PyTime_GetPerfCounter();
-            if (target <= now)
+            nanoseconds = deadline - _PyTime_GetPerfCounter();
+            if (nanoseconds <= 0) {
                 break;
-            nanoseconds = target - now;
+            }
         }
     }
     if (!mutex->locked) {
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index 9b5e273f1a8ba..12dad7e9e4427 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -438,7 +438,7 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
     dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
              lock, microseconds, intr_flag));
 
-    _PyTime_t timeout;
+    _PyTime_t timeout;  // relative timeout
     if (microseconds >= 0) {
         _PyTime_t ns;
         if (microseconds <= _PyTime_MAX / 1000) {
@@ -465,16 +465,13 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
     struct timespec abs_timeout;
     // Local scope for deadline
     {
-        _PyTime_t deadline = _PyTime_GetMonotonicClock() + timeout;
+        _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
         _PyTime_AsTimespec_clamp(deadline, &abs_timeout);
     }
 #else
     _PyTime_t deadline = 0;
-    if (timeout > 0
-        && !intr_flag
-        )
-    {
-        deadline = _PyTime_GetMonotonicClock() + timeout;
+    if (timeout > 0 && !intr_flag) {
+        deadline = _PyDeadline_Init(timeout);
     }
 #endif
 
@@ -484,9 +481,10 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
             status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
                                               &abs_timeout));
 #else
-            _PyTime_t abs_timeout = _PyTime_GetSystemClock() + timeout;
+            _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(),
+                                             timeout);
             struct timespec ts;
-            _PyTime_AsTimespec_clamp(abs_timeout, &ts);
+            _PyTime_AsTimespec_clamp(abs_time, &ts);
             status = fix_status(sem_timedwait(thelock, &ts));
 #endif
         }
@@ -508,7 +506,7 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
 #ifndef HAVE_SEM_CLOCKWAIT
         if (timeout > 0) {
             /* wait interrupted by a signal (EINTR): recompute the timeout */
-            _PyTime_t timeout = deadline - _PyTime_GetMonotonicClock();
+            _PyTime_t timeout = _PyDeadline_Get(deadline);
             if (timeout < 0) {
                 status = ETIMEDOUT;
                 break;



More information about the Python-checkins mailing list