Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Include/cpython/pytime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
195 changes: 96 additions & 99 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand All @@ -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;
}


Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
}
Expand All @@ -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;
Expand All @@ -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)";
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down