Skip to content

Commit d62d925

Browse files
authored
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.
1 parent 09796f2 commit d62d925

File tree

2 files changed

+101
-102
lines changed

2 files changed

+101
-102
lines changed

Include/cpython/pytime.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,11 @@ PyAPI_FUNC(_PyTime_t) _PyTime_As100Nanoseconds(_PyTime_t t,
125125
object. */
126126
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
127127

128+
#ifndef MS_WINDOWS
128129
/* Create a timestamp from a timeval structure.
129130
Raise an exception and return -1 on overflow, return 0 on success. */
130131
PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv);
132+
#endif
131133

132134
/* Convert a timestamp to a timeval structure (microsecond resolution).
133135
tv_usec is always positive.
@@ -188,7 +190,7 @@ typedef struct {
188190
189191
If the internal clock fails, silently ignore the error and return 0.
190192
On integer overflow, silently ignore the overflow and clamp the clock to
191-
_PyTime_MIN or _PyTime_MAX.
193+
[_PyTime_MIN; _PyTime_MAX].
192194
193195
Use _PyTime_GetSystemClockWithInfo() to check for failure. */
194196
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
@@ -208,7 +210,7 @@ PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
208210
209211
If the internal clock fails, silently ignore the error and return 0.
210212
On integer overflow, silently ignore the overflow and clamp the clock to
211-
_PyTime_MIN or _PyTime_MAX.
213+
[_PyTime_MIN; _PyTime_MAX].
212214
213215
Use _PyTime_GetMonotonicClockWithInfo() to check for failure. */
214216
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
@@ -239,7 +241,7 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
239241
240242
If the internal clock fails, silently ignore the error and return 0.
241243
On integer overflow, silently ignore the overflow and clamp the clock to
242-
_PyTime_MIN or _PyTime_MAX.
244+
[_PyTime_MIN; _PyTime_MAX].
243245
244246
Use _PyTime_GetPerfCounterWithInfo() to check for failure. */
245247
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);

Python/pytime.c

+96-99
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
#endif
1414
#endif
1515

16-
#define _PyTime_check_mul_overflow(a, b) \
17-
(assert(b > 0), \
18-
(_PyTime_t)(a) < _PyTime_MIN / (_PyTime_t)(b) \
19-
|| _PyTime_MAX / (_PyTime_t)(b) < (_PyTime_t)(a))
20-
2116
/* To millisecond (10^-3) */
2217
#define SEC_TO_MS 1000
2318

@@ -78,6 +73,49 @@ pytime_as_nanoseconds(_PyTime_t t)
7873
}
7974

8075

76+
// Compute t + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
77+
static inline _PyTime_t
78+
pytime_add(_PyTime_t *t, _PyTime_t t2)
79+
{
80+
if (t2 > 0 && *t > _PyTime_MAX - t2) {
81+
*t = _PyTime_MAX;
82+
return -1;
83+
}
84+
else if (t2 < 0 && *t < _PyTime_MIN - t2) {
85+
*t = _PyTime_MIN;
86+
return -1;
87+
}
88+
else {
89+
*t += t2;
90+
return 0;
91+
}
92+
}
93+
94+
95+
static inline int
96+
_PyTime_check_mul_overflow(_PyTime_t a, _PyTime_t b)
97+
{
98+
assert(b > 0);
99+
return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
100+
}
101+
102+
103+
// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
104+
static inline _PyTime_t
105+
pytime_mul(_PyTime_t *t, _PyTime_t k)
106+
{
107+
assert(k > 0);
108+
if (_PyTime_check_mul_overflow(*t, k)) {
109+
*t = (*t >= 0) ? _PyTime_MAX : _PyTime_MIN;
110+
return -1;
111+
}
112+
else {
113+
*t *= k;
114+
return 0;
115+
}
116+
}
117+
118+
81119
_PyTime_t
82120
_PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div)
83121
{
@@ -371,41 +409,25 @@ _PyTime_FromNanosecondsObject(_PyTime_t *tp, PyObject *obj)
371409

372410
#ifdef HAVE_CLOCK_GETTIME
373411
static int
374-
pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise)
412+
pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc)
375413
{
376414
_PyTime_t t, tv_nsec;
377-
int res = 0;
378415

379416
Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
380417
t = (_PyTime_t)ts->tv_sec;
381418

382-
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
383-
if (raise) {
384-
pytime_overflow();
385-
res = -1;
386-
}
387-
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
388-
}
389-
else {
390-
t = t * SEC_TO_NS;
391-
}
419+
int res1 = pytime_mul(&t, SEC_TO_NS);
392420

393421
tv_nsec = ts->tv_nsec;
394-
/* The following test is written for positive only tv_nsec */
395-
assert(tv_nsec >= 0);
396-
if (t > _PyTime_MAX - tv_nsec) {
397-
if (raise) {
398-
pytime_overflow();
399-
res = -1;
400-
}
401-
t = _PyTime_MAX;
402-
}
403-
else {
404-
t += tv_nsec;
405-
}
422+
int res2 = pytime_add(&t, tv_nsec);
406423

407424
*tp = pytime_from_nanoseconds(t);
408-
return res;
425+
426+
if (raise_exc && (res1 < 0 || res2 < 0)) {
427+
pytime_overflow();
428+
return -1;
429+
}
430+
return 0;
409431
}
410432

411433
int
@@ -416,43 +438,25 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
416438
#endif
417439

418440

419-
#if !defined(MS_WINDOWS)
441+
#ifndef MS_WINDOWS
420442
static int
421-
pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise)
443+
pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise_exc)
422444
{
423-
_PyTime_t t, usec;
424-
int res = 0;
425-
426445
Py_BUILD_ASSERT(sizeof(tv->tv_sec) <= sizeof(_PyTime_t));
427-
t = (_PyTime_t)tv->tv_sec;
446+
_PyTime_t t = (_PyTime_t)tv->tv_sec;
428447

429-
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
430-
if (raise) {
431-
pytime_overflow();
432-
res = -1;
433-
}
434-
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
435-
}
436-
else {
437-
t = t * SEC_TO_NS;
438-
}
448+
int res1 = pytime_mul(&t, SEC_TO_NS);
439449

440-
usec = (_PyTime_t)tv->tv_usec * US_TO_NS;
441-
/* The following test is written for positive only usec */
442-
assert(usec >= 0);
443-
if (t > _PyTime_MAX - usec) {
444-
if (raise) {
445-
pytime_overflow();
446-
res = -1;
447-
}
448-
t = _PyTime_MAX;
449-
}
450-
else {
451-
t += usec;
452-
}
450+
_PyTime_t usec = (_PyTime_t)tv->tv_usec * US_TO_NS;
451+
int res2 = pytime_add(&t, usec);
453452

454453
*tp = pytime_from_nanoseconds(t);
455-
return res;
454+
455+
if (raise_exc && (res1 < 0 || res2 < 0)) {
456+
pytime_overflow();
457+
return -1;
458+
}
459+
return 0;
456460
}
457461

458462

@@ -572,7 +576,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
572576
assert(k > 1);
573577
if (t >= 0) {
574578
// Don't use (t + k - 1) / k to avoid integer overflow
575-
// if t=_PyTime_MAX
579+
// if t is equal to _PyTime_MAX
576580
_PyTime_t q = t / k;
577581
if (t % k) {
578582
q += 1;
@@ -581,7 +585,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
581585
}
582586
else {
583587
// Don't use (t - (k - 1)) / k to avoid integer overflow
584-
// if t=_PyTime_MIN
588+
// if t is equals to _PyTime_MIN.
585589
_PyTime_t q = t / k;
586590
if (t % k) {
587591
q -= 1;
@@ -804,14 +808,14 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
804808

805809

806810
static int
807-
py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
811+
py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
808812
{
813+
assert(info == NULL || raise_exc);
814+
809815
#ifdef MS_WINDOWS
810816
FILETIME system_time;
811817
ULARGE_INTEGER large;
812818

813-
assert(info == NULL || raise);
814-
815819
GetSystemTimeAsFileTime(&system_time);
816820
large.u.LowPart = system_time.dwLowDateTime;
817821
large.u.HighPart = system_time.dwHighDateTime;
@@ -846,8 +850,6 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
846850
struct timeval tv;
847851
#endif
848852

849-
assert(info == NULL || raise);
850-
851853
#ifdef HAVE_CLOCK_GETTIME
852854

853855
#ifdef HAVE_CLOCK_GETTIME_RUNTIME
@@ -856,12 +858,12 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
856858

857859
err = clock_gettime(CLOCK_REALTIME, &ts);
858860
if (err) {
859-
if (raise) {
861+
if (raise_exc) {
860862
PyErr_SetFromErrno(PyExc_OSError);
861863
}
862864
return -1;
863865
}
864-
if (pytime_fromtimespec(tp, &ts, raise) < 0) {
866+
if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
865867
return -1;
866868
}
867869

@@ -890,12 +892,12 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
890892
/* test gettimeofday() */
891893
err = gettimeofday(&tv, (struct timezone *)NULL);
892894
if (err) {
893-
if (raise) {
895+
if (raise_exc) {
894896
PyErr_SetFromErrno(PyExc_OSError);
895897
}
896898
return -1;
897899
}
898-
if (pytime_fromtimeval(tp, &tv, raise) < 0) {
900+
if (pytime_fromtimeval(tp, &tv, raise_exc) < 0) {
899901
return -1;
900902
}
901903

@@ -987,28 +989,21 @@ py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise)
987989

988990

989991
static int
990-
py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
992+
py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
991993
{
992-
#if defined(MS_WINDOWS)
993-
ULONGLONG ticks;
994-
_PyTime_t t;
995-
996-
assert(info == NULL || raise);
994+
assert(info == NULL || raise_exc);
997995

998-
ticks = GetTickCount64();
996+
#if defined(MS_WINDOWS)
997+
ULONGLONG ticks = GetTickCount64();
999998
Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t));
1000-
t = (_PyTime_t)ticks;
999+
_PyTime_t t = (_PyTime_t)ticks;
10011000

1002-
if (_PyTime_check_mul_overflow(t, MS_TO_NS)) {
1003-
if (raise) {
1004-
pytime_overflow();
1005-
return -1;
1006-
}
1007-
// Clamp to _PyTime_MAX silently.
1008-
*tp = _PyTime_MAX;
1009-
}
1010-
else {
1011-
*tp = t * MS_TO_NS;
1001+
int res = pytime_mul(&t, MS_TO_NS);
1002+
*tp = t;
1003+
1004+
if (raise_exc && res < 0) {
1005+
pytime_overflow();
1006+
return -1;
10121007
}
10131008

10141009
if (info) {
@@ -1030,7 +1025,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
10301025
static _PyTime_t timebase_numer = 0;
10311026
static _PyTime_t timebase_denom = 0;
10321027
if (timebase_denom == 0) {
1033-
if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise) < 0) {
1028+
if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise_exc) < 0) {
10341029
return -1;
10351030
}
10361031
}
@@ -1055,7 +1050,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
10551050

10561051
time = gethrtime();
10571052
if (time == -1) {
1058-
if (raise) {
1053+
if (raise_exc) {
10591054
PyErr_SetFromErrno(PyExc_OSError);
10601055
}
10611056
return -1;
@@ -1071,7 +1066,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
10711066
}
10721067

10731068
#else
1074-
struct timespec ts;
1069+
10751070
#ifdef CLOCK_HIGHRES
10761071
const clockid_t clk_id = CLOCK_HIGHRES;
10771072
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)
10801075
const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
10811076
#endif
10821077

1083-
assert(info == NULL || raise);
1084-
1078+
struct timespec ts;
10851079
if (clock_gettime(clk_id, &ts) != 0) {
1086-
if (raise) {
1080+
if (raise_exc) {
10871081
PyErr_SetFromErrno(PyExc_OSError);
10881082
return -1;
10891083
}
10901084
return -1;
10911085
}
10921086

1087+
if (pytime_fromtimespec(tp, &ts, raise_exc) < 0) {
1088+
return -1;
1089+
}
1090+
10931091
if (info) {
1094-
struct timespec res;
10951092
info->monotonic = 1;
10961093
info->implementation = implementation;
10971094
info->adjustable = 0;
1095+
struct timespec res;
10981096
if (clock_getres(clk_id, &res) != 0) {
10991097
PyErr_SetFromErrno(PyExc_OSError);
11001098
return -1;
11011099
}
11021100
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
11031101
}
1104-
if (pytime_fromtimespec(tp, &ts, raise) < 0) {
1105-
return -1;
1106-
}
11071102
#endif
11081103
return 0;
11091104
}
@@ -1169,6 +1164,8 @@ py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise)
11691164
static int
11701165
py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
11711166
{
1167+
assert(info == NULL || raise_exc);
1168+
11721169
static LONGLONG frequency = 0;
11731170
if (frequency == 0) {
11741171
if (py_win_perf_counter_frequency(&frequency, raise) < 0) {

0 commit comments

Comments
 (0)