From d62d925823b005c33b432e527562b573a3a89635 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Sep 2021 03:07:11 +0200 Subject: 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. --- Include/cpython/pytime.h | 8 +- Python/pytime.c | 195 +++++++++++++++++++++++------------------------ 2 files changed, 101 insertions(+), 102 deletions(-) diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h index 04c43ac..b5a3513 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 f6ec191..1959615 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) { -- cgit v0.12