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.
This commit is contained in:
Victor Stinner 2021-09-30 03:07:11 +02:00 committed by GitHub
parent 09796f2f14
commit d62d925823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 101 additions and 102 deletions

View file

@ -125,9 +125,11 @@ PyAPI_FUNC(_PyTime_t) _PyTime_As100Nanoseconds(_PyTime_t t,
object. */ object. */
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t); PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
#ifndef MS_WINDOWS
/* Create a timestamp from a timeval structure. /* Create a timestamp from a timeval structure.
Raise an exception and return -1 on overflow, return 0 on success. */ Raise an exception and return -1 on overflow, return 0 on success. */
PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv); PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv);
#endif
/* Convert a timestamp to a timeval structure (microsecond resolution). /* Convert a timestamp to a timeval structure (microsecond resolution).
tv_usec is always positive. tv_usec is always positive.
@ -188,7 +190,7 @@ typedef struct {
If the internal clock fails, silently ignore the error and return 0. If the internal clock fails, silently ignore the error and return 0.
On integer overflow, silently ignore the overflow and clamp the clock to 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. */ Use _PyTime_GetSystemClockWithInfo() to check for failure. */
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void); 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. If the internal clock fails, silently ignore the error and return 0.
On integer overflow, silently ignore the overflow and clamp the clock to 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. */ Use _PyTime_GetMonotonicClockWithInfo() to check for failure. */
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void); 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. If the internal clock fails, silently ignore the error and return 0.
On integer overflow, silently ignore the overflow and clamp the clock to 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. */ Use _PyTime_GetPerfCounterWithInfo() to check for failure. */
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void); PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);

View file

@ -13,11 +13,6 @@
#endif #endif
#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) */ /* To millisecond (10^-3) */
#define SEC_TO_MS 1000 #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_t
_PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div) _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 #ifdef HAVE_CLOCK_GETTIME
static int 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; _PyTime_t t, tv_nsec;
int res = 0;
Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
t = (_PyTime_t)ts->tv_sec; t = (_PyTime_t)ts->tv_sec;
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { int res1 = pytime_mul(&t, SEC_TO_NS);
if (raise) {
pytime_overflow();
res = -1;
}
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
}
else {
t = t * SEC_TO_NS;
}
tv_nsec = ts->tv_nsec; tv_nsec = ts->tv_nsec;
/* The following test is written for positive only tv_nsec */ int res2 = pytime_add(&t, 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;
}
*tp = pytime_from_nanoseconds(t); *tp = pytime_from_nanoseconds(t);
return res;
if (raise_exc && (res1 < 0 || res2 < 0)) {
pytime_overflow();
return -1;
}
return 0;
} }
int int
@ -416,43 +438,25 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
#endif #endif
#if !defined(MS_WINDOWS) #ifndef MS_WINDOWS
static int 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)); 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)) { int res1 = pytime_mul(&t, SEC_TO_NS);
if (raise) {
pytime_overflow();
res = -1;
}
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
}
else {
t = t * SEC_TO_NS;
}
usec = (_PyTime_t)tv->tv_usec * US_TO_NS; _PyTime_t usec = (_PyTime_t)tv->tv_usec * US_TO_NS;
/* The following test is written for positive only usec */ int res2 = pytime_add(&t, usec);
assert(usec >= 0);
if (t > _PyTime_MAX - usec) {
if (raise) {
pytime_overflow();
res = -1;
}
t = _PyTime_MAX;
}
else {
t += usec;
}
*tp = pytime_from_nanoseconds(t); *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); assert(k > 1);
if (t >= 0) { if (t >= 0) {
// Don't use (t + k - 1) / k to avoid integer overflow // 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; _PyTime_t q = t / k;
if (t % k) { if (t % k) {
q += 1; q += 1;
@ -581,7 +585,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
} }
else { else {
// Don't use (t - (k - 1)) / k to avoid integer overflow // 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; _PyTime_t q = t / k;
if (t % k) { if (t % k) {
q -= 1; q -= 1;
@ -804,14 +808,14 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
static int 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 #ifdef MS_WINDOWS
FILETIME system_time; FILETIME system_time;
ULARGE_INTEGER large; ULARGE_INTEGER large;
assert(info == NULL || raise);
GetSystemTimeAsFileTime(&system_time); GetSystemTimeAsFileTime(&system_time);
large.u.LowPart = system_time.dwLowDateTime; large.u.LowPart = system_time.dwLowDateTime;
large.u.HighPart = system_time.dwHighDateTime; 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; struct timeval tv;
#endif #endif
assert(info == NULL || raise);
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
#ifdef HAVE_CLOCK_GETTIME_RUNTIME #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); err = clock_gettime(CLOCK_REALTIME, &ts);
if (err) { if (err) {
if (raise) { if (raise_exc) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
} }
return -1; return -1;
} }
if (pytime_fromtimespec(tp, &ts, raise) < 0) { if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
return -1; return -1;
} }
@ -890,12 +892,12 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
/* test gettimeofday() */ /* test gettimeofday() */
err = gettimeofday(&tv, (struct timezone *)NULL); err = gettimeofday(&tv, (struct timezone *)NULL);
if (err) { if (err) {
if (raise) { if (raise_exc) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
} }
return -1; return -1;
} }
if (pytime_fromtimeval(tp, &tv, raise) < 0) { if (pytime_fromtimeval(tp, &tv, raise_exc) < 0) {
return -1; return -1;
} }
@ -987,28 +989,21 @@ py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise)
static int 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)
{ {
assert(info == NULL || raise_exc);
#if defined(MS_WINDOWS) #if defined(MS_WINDOWS)
ULONGLONG ticks; ULONGLONG ticks = GetTickCount64();
_PyTime_t t;
assert(info == NULL || raise);
ticks = GetTickCount64();
Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t)); 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)) { int res = pytime_mul(&t, MS_TO_NS);
if (raise) { *tp = t;
pytime_overflow();
return -1; if (raise_exc && res < 0) {
} pytime_overflow();
// Clamp to _PyTime_MAX silently. return -1;
*tp = _PyTime_MAX;
}
else {
*tp = t * MS_TO_NS;
} }
if (info) { 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_numer = 0;
static _PyTime_t timebase_denom = 0; static _PyTime_t timebase_denom = 0;
if (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; return -1;
} }
} }
@ -1055,7 +1050,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
time = gethrtime(); time = gethrtime();
if (time == -1) { if (time == -1) {
if (raise) { if (raise_exc) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
} }
return -1; return -1;
@ -1071,7 +1066,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
} }
#else #else
struct timespec ts;
#ifdef CLOCK_HIGHRES #ifdef CLOCK_HIGHRES
const clockid_t clk_id = CLOCK_HIGHRES; const clockid_t clk_id = CLOCK_HIGHRES;
const char *implementation = "clock_gettime(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)"; const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
#endif #endif
assert(info == NULL || raise); struct timespec ts;
if (clock_gettime(clk_id, &ts) != 0) { if (clock_gettime(clk_id, &ts) != 0) {
if (raise) { if (raise_exc) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
return -1; return -1;
} }
return -1; return -1;
} }
if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
return -1;
}
if (info) { if (info) {
struct timespec res;
info->monotonic = 1; info->monotonic = 1;
info->implementation = implementation; info->implementation = implementation;
info->adjustable = 0; info->adjustable = 0;
struct timespec res;
if (clock_getres(clk_id, &res) != 0) { if (clock_getres(clk_id, &res) != 0) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
return -1; return -1;
} }
info->resolution = res.tv_sec + res.tv_nsec * 1e-9; info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
} }
if (pytime_fromtimespec(tp, &ts, raise) < 0) {
return -1;
}
#endif #endif
return 0; return 0;
} }
@ -1169,6 +1164,8 @@ py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise)
static int static int
py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise) py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
{ {
assert(info == NULL || raise_exc);
static LONGLONG frequency = 0; static LONGLONG frequency = 0;
if (frequency == 0) { if (frequency == 0) {
if (py_win_perf_counter_frequency(&frequency, raise) < 0) { if (py_win_perf_counter_frequency(&frequency, raise) < 0) {