[Python-checkins] bpo-41710: Add _PyTime_AsTimespec_clamp() (GH-28629)

vstinner webhook-mailer at python.org
Wed Sep 29 20:11:50 EDT 2021


https://github.com/python/cpython/commit/09796f2f142fdb1214f34a3ca917959ecb32a88b
commit: 09796f2f142fdb1214f34a3ca917959ecb32a88b
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-09-30T02:11:41+02:00
summary:

bpo-41710: Add _PyTime_AsTimespec_clamp() (GH-28629)

Add the _PyTime_AsTimespec_clamp() function: similar to
_PyTime_AsTimespec(), but clamp to _PyTime_t min/max and don't raise
an exception.

PyThread_acquire_lock_timed() now uses _PyTime_AsTimespec_clamp() to
remove the Py_UNREACHABLE() code path.

* Add _PyTime_AsTime_t() function.
* Add PY_TIME_T_MIN and PY_TIME_T_MAX constants.
* Replace _PyTime_AsTimeval_noraise() with _PyTime_AsTimeval_clamp().
* Add pytime_divide_round_up() function.
* Fix integer overflow in pytime_divide().
* Add pytime_divmod() function.

files:
M Include/cpython/pytime.h
M Lib/test/test_time.py
M Modules/_ssl.c
M Modules/_testcapimodule.c
M Modules/selectmodule.c
M Modules/socketmodule.c
M Python/pytime.c
M Python/thread_pthread.h

diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h
index 8c2958501f796..04c43ac5d4d6c 100644
--- a/Include/cpython/pytime.h
+++ b/Include/cpython/pytime.h
@@ -16,6 +16,7 @@ extern "C" {
 typedef int64_t _PyTime_t;
 #define _PyTime_MIN INT64_MIN
 #define _PyTime_MAX INT64_MAX
+#define _SIZEOF_PYTIME_T 8
 
 typedef enum {
     /* Round towards minus infinity (-inf).
@@ -136,8 +137,9 @@ PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
     struct timeval *tv,
     _PyTime_round_t round);
 
-/* Similar to _PyTime_AsTimeval(), but don't raise an exception on error. */
-PyAPI_FUNC(int) _PyTime_AsTimeval_noraise(_PyTime_t t,
+/* Similar to _PyTime_AsTimeval() but don't raise an exception on overflow.
+   On overflow, clamp tv_sec to _PyTime_t min/max. */
+PyAPI_FUNC(void) _PyTime_AsTimeval_clamp(_PyTime_t t,
     struct timeval *tv,
     _PyTime_round_t round);
 
@@ -162,6 +164,10 @@ PyAPI_FUNC(int) _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts);
    tv_nsec is always positive.
    Raise an exception and return -1 on error, return 0 on success. */
 PyAPI_FUNC(int) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts);
+
+/* Similar to _PyTime_AsTimespec() but don't raise an exception on overflow.
+   On overflow, clamp tv_sec to _PyTime_t min/max. */
+PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
 #endif
 
 /* Compute ticks * mul / div.
@@ -181,7 +187,7 @@ typedef struct {
 /* Get the current time from the system clock.
 
    If the internal clock fails, silently ignore the error and return 0.
-   On integer overflow, silently ignore the overflow and truncated the clock to
+   On integer overflow, silently ignore the overflow and clamp the clock to
    _PyTime_MIN or _PyTime_MAX.
 
    Use _PyTime_GetSystemClockWithInfo() to check for failure. */
@@ -201,7 +207,7 @@ PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
    results of consecutive calls is valid.
 
    If the internal clock fails, silently ignore the error and return 0.
-   On integer overflow, silently ignore the overflow and truncated the clock to
+   On integer overflow, silently ignore the overflow and clamp the clock to
    _PyTime_MIN or _PyTime_MAX.
 
    Use _PyTime_GetMonotonicClockWithInfo() to check for failure. */
@@ -232,7 +238,7 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
    measure a short duration.
 
    If the internal clock fails, silently ignore the error and return 0.
-   On integer overflow, silently ignore the overflow and truncated the clock to
+   On integer overflow, silently ignore the overflow and clamp the clock to
    _PyTime_MIN or _PyTime_MAX.
 
    Use _PyTime_GetPerfCounterWithInfo() to check for failure. */
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 325829864851c..f7fd6510d8aa7 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -38,6 +38,10 @@ class _PyTime(enum.IntEnum):
     # Round away from zero
     ROUND_UP = 3
 
+# _PyTime_t is int64_t
+_PyTime_MIN = -2 ** 63
+_PyTime_MAX = 2 ** 63 - 1
+
 # Rounding modes supported by PyTime
 ROUNDING_MODES = (
     # (PyTime rounding method, decimal rounding method)
@@ -960,6 +964,49 @@ def timespec_converter(ns):
                                 NS_TO_SEC,
                                 value_filter=self.time_t_filter)
 
+    @unittest.skipUnless(hasattr(_testcapi, 'PyTime_AsTimeval_clamp'),
+                         'need _testcapi.PyTime_AsTimeval_clamp')
+    def test_AsTimeval_clamp(self):
+        from _testcapi import PyTime_AsTimeval_clamp
+
+        if sys.platform == 'win32':
+            from _testcapi import LONG_MIN, LONG_MAX
+            tv_sec_max = LONG_MAX
+            tv_sec_min = LONG_MIN
+        else:
+            tv_sec_max = self.time_t_max
+            tv_sec_min = self.time_t_min
+
+        for t in (_PyTime_MIN, _PyTime_MAX):
+            ts = PyTime_AsTimeval_clamp(t, _PyTime.ROUND_CEILING)
+            with decimal.localcontext() as context:
+                context.rounding = decimal.ROUND_CEILING
+                us = self.decimal_round(decimal.Decimal(t) / US_TO_NS)
+            tv_sec, tv_usec = divmod(us, SEC_TO_US)
+            if tv_sec_max < tv_sec:
+                tv_sec = tv_sec_max
+                tv_usec = 0
+            elif tv_sec < tv_sec_min:
+                tv_sec = tv_sec_min
+                tv_usec = 0
+            self.assertEqual(ts, (tv_sec, tv_usec))
+
+    @unittest.skipUnless(hasattr(_testcapi, 'PyTime_AsTimespec_clamp'),
+                         'need _testcapi.PyTime_AsTimespec_clamp')
+    def test_AsTimespec_clamp(self):
+        from _testcapi import PyTime_AsTimespec_clamp
+
+        for t in (_PyTime_MIN, _PyTime_MAX):
+            ts = PyTime_AsTimespec_clamp(t)
+            tv_sec, tv_nsec = divmod(t, NS_TO_SEC)
+            if self.time_t_max < tv_sec:
+                tv_sec = self.time_t_max
+                tv_nsec = 0
+            elif tv_sec < self.time_t_min:
+                tv_sec = self.time_t_min
+                tv_nsec = 0
+            self.assertEqual(ts, (tv_sec, tv_nsec))
+
     def test_AsMilliseconds(self):
         from _testcapi import PyTime_AsMilliseconds
 
@@ -1062,7 +1109,7 @@ def test_clock_functions(self):
         clock_names = [
             "CLOCK_MONOTONIC", "clock_gettime", "clock_gettime_ns", "clock_settime",
             "clock_settime_ns", "clock_getres"]
-        
+
         if mac_ver >= (10, 12):
             for name in clock_names:
                 self.assertTrue(hasattr(time, name), f"time.{name} is not available")
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 6c63301b2a7d8..411314f6d0f01 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2264,7 +2264,7 @@ PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout)
     if (!_PyIsSelectable_fd(s->sock_fd))
         return SOCKET_TOO_LARGE_FOR_SELECT;
 
-    _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
+    _PyTime_AsTimeval_clamp(timeout, &tv, _PyTime_ROUND_CEILING);
 
     FD_ZERO(&fds);
     FD_SET(s->sock_fd, &fds);
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 51323f03e2368..e3eec0c47f73a 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4687,7 +4687,32 @@ test_PyTime_AsTimeval(PyObject *self, PyObject *args)
     if (seconds == NULL) {
         return NULL;
     }
-    return Py_BuildValue("Nl", seconds, tv.tv_usec);
+    return Py_BuildValue("Nl", seconds, (long)tv.tv_usec);
+}
+
+static PyObject *
+test_PyTime_AsTimeval_clamp(PyObject *self, PyObject *args)
+{
+    PyObject *obj;
+    int round;
+    if (!PyArg_ParseTuple(args, "Oi", &obj, &round)) {
+        return NULL;
+    }
+    if (check_time_rounding(round) < 0) {
+        return NULL;
+    }
+    _PyTime_t t;
+    if (_PyTime_FromNanosecondsObject(&t, obj) < 0) {
+        return NULL;
+    }
+    struct timeval tv;
+    _PyTime_AsTimeval_clamp(t, &tv, round);
+
+    PyObject *seconds = PyLong_FromLongLong(tv.tv_sec);
+    if (seconds == NULL) {
+        return NULL;
+    }
+    return Py_BuildValue("Nl", seconds, (long)tv.tv_usec);
 }
 
 #ifdef HAVE_CLOCK_GETTIME
@@ -4708,6 +4733,22 @@ test_PyTime_AsTimespec(PyObject *self, PyObject *args)
     }
     return Py_BuildValue("Nl", _PyLong_FromTime_t(ts.tv_sec), ts.tv_nsec);
 }
+
+static PyObject *
+test_PyTime_AsTimespec_clamp(PyObject *self, PyObject *args)
+{
+    PyObject *obj;
+    if (!PyArg_ParseTuple(args, "O", &obj)) {
+        return NULL;
+    }
+    _PyTime_t t;
+    if (_PyTime_FromNanosecondsObject(&t, obj) < 0) {
+        return NULL;
+    }
+    struct timespec ts;
+    _PyTime_AsTimespec_clamp(t, &ts);
+    return Py_BuildValue("Nl", _PyLong_FromTime_t(ts.tv_sec), ts.tv_nsec);
+}
 #endif
 
 static PyObject *
@@ -5872,8 +5913,10 @@ static PyMethodDef TestMethods[] = {
     {"PyTime_FromSecondsObject", test_pytime_fromsecondsobject,  METH_VARARGS},
     {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
     {"PyTime_AsTimeval", test_PyTime_AsTimeval, METH_VARARGS},
+    {"PyTime_AsTimeval_clamp", test_PyTime_AsTimeval_clamp, METH_VARARGS},
 #ifdef HAVE_CLOCK_GETTIME
     {"PyTime_AsTimespec", test_PyTime_AsTimespec, METH_VARARGS},
+    {"PyTime_AsTimespec_clamp", test_PyTime_AsTimespec_clamp, METH_VARARGS},
 #endif
     {"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
     {"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 3ecd0c32b3038..b71b2c4f6c037 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -344,7 +344,7 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
                 n = 0;
                 break;
             }
-            _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
+            _PyTime_AsTimeval_clamp(timeout, &tv, _PyTime_ROUND_CEILING);
             /* retry select() with the recomputed timeout */
         }
     } while (1);
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 83f05b72ecbd9..f474869c94dc2 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -758,7 +758,7 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval,
     Py_END_ALLOW_THREADS;
 #else
     if (interval >= 0) {
-        _PyTime_AsTimeval_noraise(interval, &tv, _PyTime_ROUND_CEILING);
+        _PyTime_AsTimeval_clamp(interval, &tv, _PyTime_ROUND_CEILING);
         tvp = &tv;
     }
     else
diff --git a/Python/pytime.c b/Python/pytime.c
index b47a573488b6d..f6ec191b637f0 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -35,6 +35,16 @@
 #define NS_TO_US (1000)
 #define NS_TO_100NS (100)
 
+#if SIZEOF_TIME_T == SIZEOF_LONG_LONG
+#  define PY_TIME_T_MAX LLONG_MAX
+#  define PY_TIME_T_MIN LLONG_MIN
+#elif SIZEOF_TIME_T == SIZEOF_LONG
+#  define PY_TIME_T_MAX LONG_MAX
+#  define PY_TIME_T_MIN LONG_MIN
+#else
+#  error "unsupported time_t size"
+#endif
+
 
 static void
 pytime_time_t_overflow(void)
@@ -63,7 +73,7 @@ pytime_from_nanoseconds(_PyTime_t t)
 static inline _PyTime_t
 pytime_as_nanoseconds(_PyTime_t t)
 {
-    // _PyTime_t is a number of nanoseconds
+    // _PyTime_t is a number of nanoseconds: see pytime_from_nanoseconds()
     return t;
 }
 
@@ -119,6 +129,48 @@ _PyLong_FromTime_t(time_t t)
 }
 
 
+// Convert _PyTime_t to time_t.
+// Return 0 on success. Return -1 and clamp the value on overflow.
+static int
+_PyTime_AsTime_t(_PyTime_t t, time_t *t2)
+{
+#if SIZEOF_TIME_T < _SIZEOF_PYTIME_T
+    if ((_PyTime_t)PY_TIME_T_MAX < t) {
+        *t2 = PY_TIME_T_MAX;
+        return -1;
+    }
+    if (t < (_PyTime_t)PY_TIME_T_MIN) {
+        *t2 = PY_TIME_T_MIN;
+        return -1;
+    }
+#endif
+    *t2 = (time_t)t;
+    return 0;
+}
+
+
+#ifdef MS_WINDOWS
+// Convert _PyTime_t to long.
+// Return 0 on success. Return -1 and clamp the value on overflow.
+static int
+_PyTime_AsLong(_PyTime_t t, long *t2)
+{
+#if SIZEOF_LONG < _SIZEOF_PYTIME_T
+    if ((_PyTime_t)LONG_MAX < t) {
+        *t2 = LONG_MAX;
+        return -1;
+    }
+    if (t < (_PyTime_t)LONG_MIN) {
+        *t2 = LONG_MIN;
+        return -1;
+    }
+#endif
+    *t2 = (long)t;
+    return 0;
+}
+#endif
+
+
 /* Round to nearest with ties going to nearest even integer
    (_PyTime_ROUND_HALF_EVEN) */
 static double
@@ -514,16 +566,40 @@ _PyTime_AsNanosecondsObject(_PyTime_t t)
 }
 
 
+static _PyTime_t
+pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
+{
+    assert(k > 1);
+    if (t >= 0) {
+        // Don't use (t + k - 1) / k to avoid integer overflow
+        // if t=_PyTime_MAX
+        _PyTime_t q = t / k;
+        if (t % k) {
+            q += 1;
+        }
+        return q;
+    }
+    else {
+        // Don't use (t - (k - 1)) / k to avoid integer overflow
+        // if t=_PyTime_MIN
+        _PyTime_t q = t / k;
+        if (t % k) {
+            q -= 1;
+        }
+        return q;
+    }
+}
+
+
 static _PyTime_t
 pytime_divide(const _PyTime_t t, const _PyTime_t k,
               const _PyTime_round_t round)
 {
     assert(k > 1);
     if (round == _PyTime_ROUND_HALF_EVEN) {
-        _PyTime_t x, r, abs_r;
-        x = t / k;
-        r = t % k;
-        abs_r = Py_ABS(r);
+        _PyTime_t x = t / k;
+        _PyTime_t r = t % k;
+        _PyTime_t abs_r = Py_ABS(r);
         if (abs_r > k / 2 || (abs_r == k / 2 && (Py_ABS(x) & 1))) {
             if (t >= 0) {
                 x++;
@@ -536,7 +612,7 @@ pytime_divide(const _PyTime_t t, const _PyTime_t k,
     }
     else if (round == _PyTime_ROUND_CEILING) {
         if (t >= 0) {
-            return (t + k - 1) / k;
+            return pytime_divide_round_up(t, k);
         }
         else {
             return t / k;
@@ -547,18 +623,41 @@ pytime_divide(const _PyTime_t t, const _PyTime_t k,
             return t / k;
         }
         else {
-            return (t - (k - 1)) / k;
+            return pytime_divide_round_up(t, k);
         }
     }
     else {
         assert(round == _PyTime_ROUND_UP);
-        if (t >= 0) {
-            return (t + k - 1) / k;
-        }
-        else {
-            return (t - (k - 1)) / k;
+        return pytime_divide_round_up(t, k);
+    }
+}
+
+
+// Compute (t / k, t % k) in (pq, pr).
+// Make sure that 0 <= pr < k.
+// Return 0 on success.
+// Return -1 on underflow and store (_PyTime_MIN, 0) in (pq, pr).
+static int
+pytime_divmod(const _PyTime_t t, const _PyTime_t k,
+              _PyTime_t *pq, _PyTime_t *pr)
+{
+    assert(k > 1);
+    _PyTime_t q = t / k;
+    _PyTime_t r = t % k;
+    if (r < 0) {
+        if (q == _PyTime_MIN) {
+            *pq = _PyTime_MIN;
+            *pr = 0;
+            return -1;
         }
+        r += k;
+        q -= 1;
     }
+    assert(0 <= r && r < k);
+
+    *pq = q;
+    *pr = r;
+    return 0;
 }
 
 
@@ -596,64 +695,41 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
 
 
 static int
-pytime_as_timeval(_PyTime_t t, _PyTime_t *p_secs, int *p_us,
+pytime_as_timeval(_PyTime_t t, _PyTime_t *ptv_sec, int *ptv_usec,
                   _PyTime_round_t round)
 {
-    _PyTime_t ns, tv_sec;
-    ns = pytime_as_nanoseconds(t);
-    tv_sec = ns / SEC_TO_NS;
-    ns = ns % SEC_TO_NS;
-
-    int tv_usec = (int)pytime_divide(ns, US_TO_NS, round);
-    int res = 0;
-    if (tv_usec < 0) {
-        tv_usec += SEC_TO_US;
-        if (tv_sec != _PyTime_MIN) {
-            tv_sec -= 1;
-        }
-        else {
-            res = -1;
-        }
-    }
-    else if (tv_usec >= SEC_TO_US) {
-        tv_usec -= SEC_TO_US;
-        if (tv_sec != _PyTime_MAX) {
-            tv_sec += 1;
-        }
-        else {
-            res = -1;
-        }
-    }
-    assert(0 <= tv_usec && tv_usec < SEC_TO_US);
-
-    *p_secs = tv_sec;
-    *p_us = tv_usec;
+    _PyTime_t ns = pytime_as_nanoseconds(t);
+    _PyTime_t us = pytime_divide(ns, US_TO_NS, round);
 
+    _PyTime_t tv_sec, tv_usec;
+    int res = pytime_divmod(us, SEC_TO_US, &tv_sec, &tv_usec);
+    *ptv_sec = tv_sec;
+    *ptv_usec = (int)tv_usec;
     return res;
 }
 
 
 static int
 pytime_as_timeval_struct(_PyTime_t t, struct timeval *tv,
-                         _PyTime_round_t round, int raise)
+                         _PyTime_round_t round, int raise_exc)
 {
-    _PyTime_t secs, secs2;
-    int us;
-    int res;
-
-    res = pytime_as_timeval(t, &secs, &us, round);
+    _PyTime_t tv_sec;
+    int tv_usec;
+    int res = pytime_as_timeval(t, &tv_sec, &tv_usec, round);
+    int res2;
 #ifdef MS_WINDOWS
-    tv->tv_sec = (long)secs;
+    // On Windows, timeval.tv_sec type is long
+    res2 = _PyTime_AsLong(tv_sec, &tv->tv_sec);
 #else
-    tv->tv_sec = secs;
+    res2 = _PyTime_AsTime_t(tv_sec, &tv->tv_sec);
 #endif
-    tv->tv_usec = us;
+    if (res2 < 0) {
+        tv_usec = 0;
+    }
+    tv->tv_usec = tv_usec;
 
-    secs2 = (_PyTime_t)tv->tv_sec;
-    if (res < 0 || secs2 != secs) {
-        if (raise) {
-            pytime_time_t_overflow();
-        }
+    if (raise_exc && (res < 0 || res2 < 0)) {
+        pytime_time_t_overflow();
         return -1;
     }
     return 0;
@@ -667,10 +743,10 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
 }
 
 
-int
-_PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
+void
+_PyTime_AsTimeval_clamp(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
 {
-    return pytime_as_timeval_struct(t, tv, round, 0);
+    (void)pytime_as_timeval_struct(t, tv, round, 0);
 }
 
 
@@ -679,11 +755,12 @@ _PyTime_AsTimevalTime_t(_PyTime_t t, time_t *p_secs, int *us,
                         _PyTime_round_t round)
 {
     _PyTime_t secs;
-    int res = pytime_as_timeval(t, &secs, us, round);
-
-    *p_secs = (time_t)secs;
+    if (pytime_as_timeval(t, &secs, us, round) < 0) {
+        pytime_time_t_overflow();
+        return -1;
+    }
 
-    if (res < 0 || (_PyTime_t)*p_secs != secs) {
+    if (_PyTime_AsTime_t(secs, p_secs) < 0) {
         pytime_time_t_overflow();
         return -1;
     }
@@ -692,28 +769,37 @@ _PyTime_AsTimevalTime_t(_PyTime_t t, time_t *p_secs, int *us,
 
 
 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
-int
-_PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
+static int
+pytime_as_timespec(_PyTime_t t, struct timespec *ts, int raise_exc)
 {
+    _PyTime_t ns = pytime_as_nanoseconds(t);
     _PyTime_t tv_sec, tv_nsec;
+    int res = pytime_divmod(ns, SEC_TO_NS, &tv_sec, &tv_nsec);
 
-    _PyTime_t ns = pytime_as_nanoseconds(t);
-    tv_sec = ns / SEC_TO_NS;
-    tv_nsec = ns % SEC_TO_NS;
-    if (tv_nsec < 0) {
-        tv_nsec += SEC_TO_NS;
-        tv_sec -= 1;
-    }
-    ts->tv_sec = (time_t)tv_sec;
-    assert(0 <= tv_nsec && tv_nsec < SEC_TO_NS);
+    int res2 = _PyTime_AsTime_t(tv_sec, &ts->tv_sec);
+    if (res2 < 0) {
+        tv_nsec = 0;
+    }
     ts->tv_nsec = tv_nsec;
 
-    if ((_PyTime_t)ts->tv_sec != tv_sec) {
+    if (raise_exc && (res < 0 || res2 < 0)) {
         pytime_time_t_overflow();
         return -1;
     }
     return 0;
 }
+
+void
+_PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts)
+{
+    (void)pytime_as_timespec(t, ts, 0);
+}
+
+int
+_PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
+{
+    return pytime_as_timespec(t, ts, 1);
+}
 #endif
 
 
@@ -918,7 +1004,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
             pytime_overflow();
             return -1;
         }
-        // Truncate to _PyTime_MAX silently.
+        // Clamp to _PyTime_MAX silently.
         *tp = _PyTime_MAX;
     }
     else {
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index a45d842ffe73d..7f04151ca91fd 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -481,11 +481,7 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
             }
             else if (dt > 0) {
                 _PyTime_t realtime_deadline = _PyTime_GetSystemClock() + dt;
-                if (_PyTime_AsTimespec(realtime_deadline, &ts) < 0) {
-                    /* Cannot occur thanks to (microseconds > PY_TIMEOUT_MAX)
-                       check done above */
-                    Py_UNREACHABLE();
-                }
+                _PyTime_AsTimespec_clamp(realtime_deadline, &ts);
                 /* no need to update microseconds value, the code only care
                    if (microseconds > 0 or (microseconds == 0). */
             }



More information about the Python-checkins mailing list