[Python-checkins] bpo-41710: Add pytime_add() and pytime_mul() (GH-28642)

vstinner webhook-mailer at python.org
Wed Sep 29 21:07:20 EDT 2021


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

bpo-41710: Add pytime_add() and pytime_mul() (GH-28642)

Add pytime_add() and pytime_mul() functions to pytime.c to compute
t+t2 and t*k with clamping to [_PyTime_MIN; _PyTime_MAX].

Fix pytime.h: _PyTime_FromTimeval() is not implemented on Windows.

files:
M Include/cpython/pytime.h
M Python/pytime.c

diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h
index 04c43ac5d4d6c..b5a351349f85b 100644
--- a/Include/cpython/pytime.h
+++ b/Include/cpython/pytime.h
@@ -125,9 +125,11 @@ PyAPI_FUNC(_PyTime_t) _PyTime_As100Nanoseconds(_PyTime_t t,
    object. */
 PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
 
+#ifndef MS_WINDOWS
 /* Create a timestamp from a timeval structure.
    Raise an exception and return -1 on overflow, return 0 on success. */
 PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv);
+#endif
 
 /* Convert a timestamp to a timeval structure (microsecond resolution).
    tv_usec is always positive.
@@ -188,7 +190,7 @@ typedef struct {
 
    If the internal clock fails, silently ignore the error and return 0.
    On integer overflow, silently ignore the overflow and clamp the clock to
-   _PyTime_MIN or _PyTime_MAX.
+   [_PyTime_MIN; _PyTime_MAX].
 
    Use _PyTime_GetSystemClockWithInfo() to check for failure. */
 PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
@@ -208,7 +210,7 @@ PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
 
    If the internal clock fails, silently ignore the error and return 0.
    On integer overflow, silently ignore the overflow and clamp the clock to
-   _PyTime_MIN or _PyTime_MAX.
+   [_PyTime_MIN; _PyTime_MAX].
 
    Use _PyTime_GetMonotonicClockWithInfo() to check for failure. */
 PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
@@ -239,7 +241,7 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
 
    If the internal clock fails, silently ignore the error and return 0.
    On integer overflow, silently ignore the overflow and clamp the clock to
-   _PyTime_MIN or _PyTime_MAX.
+   [_PyTime_MIN; _PyTime_MAX].
 
    Use _PyTime_GetPerfCounterWithInfo() to check for failure. */
 PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
diff --git a/Python/pytime.c b/Python/pytime.c
index f6ec191b637f0..1959615f1114b 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -13,11 +13,6 @@
 #endif
 #endif
 
-#define _PyTime_check_mul_overflow(a, b) \
-    (assert(b > 0), \
-     (_PyTime_t)(a) < _PyTime_MIN / (_PyTime_t)(b) \
-     || _PyTime_MAX / (_PyTime_t)(b) < (_PyTime_t)(a))
-
 /* To millisecond (10^-3) */
 #define SEC_TO_MS 1000
 
@@ -78,6 +73,49 @@ pytime_as_nanoseconds(_PyTime_t t)
 }
 
 
+// Compute t + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+static inline _PyTime_t
+pytime_add(_PyTime_t *t, _PyTime_t t2)
+{
+    if (t2 > 0 && *t > _PyTime_MAX - t2) {
+        *t = _PyTime_MAX;
+        return -1;
+    }
+    else if (t2 < 0 && *t < _PyTime_MIN - t2) {
+        *t = _PyTime_MIN;
+        return -1;
+    }
+    else {
+        *t += t2;
+        return 0;
+    }
+}
+
+
+static inline int
+_PyTime_check_mul_overflow(_PyTime_t a, _PyTime_t b)
+{
+    assert(b > 0);
+    return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
+}
+
+
+// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+static inline _PyTime_t
+pytime_mul(_PyTime_t *t, _PyTime_t k)
+{
+    assert(k > 0);
+    if (_PyTime_check_mul_overflow(*t, k)) {
+        *t = (*t >= 0) ? _PyTime_MAX : _PyTime_MIN;
+        return -1;
+    }
+    else {
+        *t *= k;
+        return 0;
+    }
+}
+
+
 _PyTime_t
 _PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div)
 {
@@ -371,41 +409,25 @@ _PyTime_FromNanosecondsObject(_PyTime_t *tp, PyObject *obj)
 
 #ifdef HAVE_CLOCK_GETTIME
 static int
-pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise)
+pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc)
 {
     _PyTime_t t, tv_nsec;
-    int res = 0;
 
     Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
     t = (_PyTime_t)ts->tv_sec;
 
-    if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
-        if (raise) {
-            pytime_overflow();
-            res = -1;
-        }
-        t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
-    }
-    else {
-        t = t * SEC_TO_NS;
-    }
+    int res1 = pytime_mul(&t, SEC_TO_NS);
 
     tv_nsec = ts->tv_nsec;
-    /* The following test is written for positive only tv_nsec */
-    assert(tv_nsec >= 0);
-    if (t > _PyTime_MAX - tv_nsec) {
-        if (raise) {
-            pytime_overflow();
-            res = -1;
-        }
-        t = _PyTime_MAX;
-    }
-    else {
-        t += tv_nsec;
-    }
+    int res2 = pytime_add(&t, tv_nsec);
 
     *tp = pytime_from_nanoseconds(t);
-    return res;
+
+    if (raise_exc && (res1 < 0 || res2 < 0)) {
+        pytime_overflow();
+        return -1;
+    }
+    return 0;
 }
 
 int
@@ -416,43 +438,25 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
 #endif
 
 
-#if !defined(MS_WINDOWS)
+#ifndef MS_WINDOWS
 static int
-pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise)
+pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise_exc)
 {
-    _PyTime_t t, usec;
-    int res = 0;
-
     Py_BUILD_ASSERT(sizeof(tv->tv_sec) <= sizeof(_PyTime_t));
-    t = (_PyTime_t)tv->tv_sec;
+    _PyTime_t t = (_PyTime_t)tv->tv_sec;
 
-    if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
-        if (raise) {
-            pytime_overflow();
-            res = -1;
-        }
-        t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
-    }
-    else {
-        t = t * SEC_TO_NS;
-    }
+    int res1 = pytime_mul(&t, SEC_TO_NS);
 
-    usec = (_PyTime_t)tv->tv_usec * US_TO_NS;
-    /* The following test is written for positive only usec */
-    assert(usec >= 0);
-    if (t > _PyTime_MAX - usec) {
-        if (raise) {
-            pytime_overflow();
-            res = -1;
-        }
-        t = _PyTime_MAX;
-    }
-    else {
-        t += usec;
-    }
+    _PyTime_t usec = (_PyTime_t)tv->tv_usec * US_TO_NS;
+    int res2 = pytime_add(&t, usec);
 
     *tp = pytime_from_nanoseconds(t);
-    return res;
+
+    if (raise_exc && (res1 < 0 || res2 < 0)) {
+        pytime_overflow();
+        return -1;
+    }
+    return 0;
 }
 
 
@@ -572,7 +576,7 @@ 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
+        // if t is equal to _PyTime_MAX
         _PyTime_t q = t / k;
         if (t % k) {
             q += 1;
@@ -581,7 +585,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
     }
     else {
         // Don't use (t - (k - 1)) / k to avoid integer overflow
-        // if t=_PyTime_MIN
+        // if t is equals to _PyTime_MIN.
         _PyTime_t q = t / k;
         if (t % k) {
             q -= 1;
@@ -804,14 +808,14 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
 
 
 static int
-py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
+py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
 {
+    assert(info == NULL || raise_exc);
+
 #ifdef MS_WINDOWS
     FILETIME system_time;
     ULARGE_INTEGER large;
 
-    assert(info == NULL || raise);
-
     GetSystemTimeAsFileTime(&system_time);
     large.u.LowPart = system_time.dwLowDateTime;
     large.u.HighPart = system_time.dwHighDateTime;
@@ -846,8 +850,6 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
     struct timeval tv;
 #endif
 
-    assert(info == NULL || raise);
-
 #ifdef HAVE_CLOCK_GETTIME
 
 #ifdef HAVE_CLOCK_GETTIME_RUNTIME
@@ -856,12 +858,12 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
 
     err = clock_gettime(CLOCK_REALTIME, &ts);
     if (err) {
-        if (raise) {
+        if (raise_exc) {
             PyErr_SetFromErrno(PyExc_OSError);
         }
         return -1;
     }
-    if (pytime_fromtimespec(tp, &ts, raise) < 0) {
+    if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
         return -1;
     }
 
@@ -890,12 +892,12 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
      /* test gettimeofday() */
     err = gettimeofday(&tv, (struct timezone *)NULL);
     if (err) {
-        if (raise) {
+        if (raise_exc) {
             PyErr_SetFromErrno(PyExc_OSError);
         }
         return -1;
     }
-    if (pytime_fromtimeval(tp, &tv, raise) < 0) {
+    if (pytime_fromtimeval(tp, &tv, raise_exc) < 0) {
         return -1;
     }
 
@@ -987,28 +989,21 @@ py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise)
 
 
 static int
-py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
+py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
 {
-#if defined(MS_WINDOWS)
-    ULONGLONG ticks;
-    _PyTime_t t;
-
-    assert(info == NULL || raise);
+    assert(info == NULL || raise_exc);
 
-    ticks = GetTickCount64();
+#if defined(MS_WINDOWS)
+    ULONGLONG ticks = GetTickCount64();
     Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t));
-    t = (_PyTime_t)ticks;
+    _PyTime_t t = (_PyTime_t)ticks;
 
-    if (_PyTime_check_mul_overflow(t, MS_TO_NS)) {
-        if (raise) {
-            pytime_overflow();
-            return -1;
-        }
-        // Clamp to _PyTime_MAX silently.
-        *tp = _PyTime_MAX;
-    }
-    else {
-        *tp = t * MS_TO_NS;
+    int res = pytime_mul(&t, MS_TO_NS);
+    *tp = t;
+
+    if (raise_exc && res < 0) {
+        pytime_overflow();
+        return -1;
     }
 
     if (info) {
@@ -1030,7 +1025,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
     static _PyTime_t timebase_numer = 0;
     static _PyTime_t timebase_denom = 0;
     if (timebase_denom == 0) {
-        if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise) < 0) {
+        if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise_exc) < 0) {
             return -1;
         }
     }
@@ -1055,7 +1050,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
 
     time = gethrtime();
     if (time == -1) {
-        if (raise) {
+        if (raise_exc) {
             PyErr_SetFromErrno(PyExc_OSError);
         }
         return -1;
@@ -1071,7 +1066,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
     }
 
 #else
-    struct timespec ts;
+
 #ifdef CLOCK_HIGHRES
     const clockid_t clk_id = CLOCK_HIGHRES;
     const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
@@ -1080,30 +1075,30 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
     const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
 #endif
 
-    assert(info == NULL || raise);
-
+    struct timespec ts;
     if (clock_gettime(clk_id, &ts) != 0) {
-        if (raise) {
+        if (raise_exc) {
             PyErr_SetFromErrno(PyExc_OSError);
             return -1;
         }
         return -1;
     }
 
+    if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
+        return -1;
+    }
+
     if (info) {
-        struct timespec res;
         info->monotonic = 1;
         info->implementation = implementation;
         info->adjustable = 0;
+        struct timespec res;
         if (clock_getres(clk_id, &res) != 0) {
             PyErr_SetFromErrno(PyExc_OSError);
             return -1;
         }
         info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
     }
-    if (pytime_fromtimespec(tp, &ts, raise) < 0) {
-        return -1;
-    }
 #endif
     return 0;
 }
@@ -1169,6 +1164,8 @@ py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise)
 static int
 py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
 {
+    assert(info == NULL || raise_exc);
+
     static LONGLONG frequency = 0;
     if (frequency == 0) {
         if (py_win_perf_counter_frequency(&frequency, raise) < 0) {



More information about the Python-checkins mailing list