mirror of
https://github.com/python/cpython.git
synced 2025-08-22 09:45:06 +00:00
gh-110850: Add PyTime_t C API (GH-115215)
* gh-110850: Add PyTime_t C API Add PyTime_t API: * PyTime_t type. * PyTime_MIN and PyTime_MAX constants. * PyTime_AsSecondsDouble(), PyTime_Monotonic(), PyTime_PerfCounter() and PyTime_GetSystemClock() functions. Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
c39272e143
commit
879f4546bf
19 changed files with 448 additions and 114 deletions
102
Python/pytime.c
102
Python/pytime.c
|
@ -50,7 +50,7 @@
|
|||
# error "time_t is not a two's complement integer type"
|
||||
#endif
|
||||
|
||||
#if _PyTime_MIN + _PyTime_MAX != -1
|
||||
#if PyTime_MIN + PyTime_MAX != -1
|
||||
# error "_PyTime_t is not a two's complement integer type"
|
||||
#endif
|
||||
|
||||
|
@ -124,16 +124,16 @@ pytime_as_nanoseconds(_PyTime_t t)
|
|||
}
|
||||
|
||||
|
||||
// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
|
||||
// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
|
||||
static inline int
|
||||
pytime_add(_PyTime_t *t1, _PyTime_t t2)
|
||||
{
|
||||
if (t2 > 0 && *t1 > _PyTime_MAX - t2) {
|
||||
*t1 = _PyTime_MAX;
|
||||
if (t2 > 0 && *t1 > PyTime_MAX - t2) {
|
||||
*t1 = PyTime_MAX;
|
||||
return -1;
|
||||
}
|
||||
else if (t2 < 0 && *t1 < _PyTime_MIN - t2) {
|
||||
*t1 = _PyTime_MIN;
|
||||
else if (t2 < 0 && *t1 < PyTime_MIN - t2) {
|
||||
*t1 = PyTime_MIN;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
|
@ -156,7 +156,7 @@ pytime_mul_check_overflow(_PyTime_t a, _PyTime_t b)
|
|||
{
|
||||
if (b != 0) {
|
||||
assert(b > 0);
|
||||
return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
|
||||
return ((a < PyTime_MIN / b) || (PyTime_MAX / b < a));
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
|
@ -164,13 +164,13 @@ pytime_mul_check_overflow(_PyTime_t a, _PyTime_t b)
|
|||
}
|
||||
|
||||
|
||||
// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
|
||||
// Compute t * k. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
|
||||
static inline int
|
||||
pytime_mul(_PyTime_t *t, _PyTime_t k)
|
||||
{
|
||||
assert(k >= 0);
|
||||
if (pytime_mul_check_overflow(*t, k)) {
|
||||
*t = (*t >= 0) ? _PyTime_MAX : _PyTime_MIN;
|
||||
*t = (*t >= 0) ? PyTime_MAX : PyTime_MIN;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
|
@ -180,7 +180,7 @@ pytime_mul(_PyTime_t *t, _PyTime_t k)
|
|||
}
|
||||
|
||||
|
||||
// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
|
||||
// Compute t * k. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
|
||||
static inline _PyTime_t
|
||||
_PyTime_Mul(_PyTime_t t, _PyTime_t k)
|
||||
{
|
||||
|
@ -459,12 +459,12 @@ _PyTime_FromSeconds(int seconds)
|
|||
/* ensure that integer overflow cannot happen, int type should have 32
|
||||
bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_NS takes 30
|
||||
bits). */
|
||||
static_assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow");
|
||||
static_assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow");
|
||||
static_assert(INT_MAX <= PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow");
|
||||
static_assert(INT_MIN >= PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow");
|
||||
|
||||
_PyTime_t t = (_PyTime_t)seconds;
|
||||
assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS)
|
||||
|| (t < 0 && t >= _PyTime_MIN / SEC_TO_NS));
|
||||
assert((t >= 0 && t <= PyTime_MAX / SEC_TO_NS)
|
||||
|| (t < 0 && t >= PyTime_MIN / SEC_TO_NS));
|
||||
t *= SEC_TO_NS;
|
||||
return pytime_from_nanoseconds(t);
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round,
|
|||
d = pytime_round(d, round);
|
||||
|
||||
/* See comments in pytime_double_to_denominator */
|
||||
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
|
||||
if (!((double)PyTime_MIN <= d && d < -(double)PyTime_MIN)) {
|
||||
pytime_time_t_overflow();
|
||||
return -1;
|
||||
}
|
||||
|
@ -649,12 +649,12 @@ _PyTime_FromMillisecondsObject(_PyTime_t *tp, PyObject *obj, _PyTime_round_t rou
|
|||
|
||||
|
||||
double
|
||||
_PyTime_AsSecondsDouble(_PyTime_t t)
|
||||
PyTime_AsSecondsDouble(PyTime_t t)
|
||||
{
|
||||
/* volatile avoids optimization changing how numbers are rounded */
|
||||
volatile double d;
|
||||
|
||||
_PyTime_t ns = pytime_as_nanoseconds(t);
|
||||
PyTime_t ns = pytime_as_nanoseconds(t);
|
||||
if (ns % SEC_TO_NS == 0) {
|
||||
/* Divide using integers to avoid rounding issues on the integer part.
|
||||
1e-9 cannot be stored exactly in IEEE 64-bit. */
|
||||
|
@ -695,7 +695,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 is equal to _PyTime_MAX
|
||||
// if t is equal to PyTime_MAX
|
||||
_PyTime_t q = t / k;
|
||||
if (t % k) {
|
||||
q += 1;
|
||||
|
@ -704,7 +704,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 is equals to _PyTime_MIN.
|
||||
// if t is equals to PyTime_MIN.
|
||||
_PyTime_t q = t / k;
|
||||
if (t % k) {
|
||||
q -= 1;
|
||||
|
@ -759,7 +759,7 @@ pytime_divide(const _PyTime_t t, const _PyTime_t k,
|
|||
// Compute (t / k, t % k) in (pq, pr).
|
||||
// Make sure that 0 <= pr < k.
|
||||
// Return 0 on success.
|
||||
// Return -1 on underflow and store (_PyTime_MIN, 0) in (pq, pr).
|
||||
// Return -1 on underflow and store (PyTime_MIN, 0) in (pq, pr).
|
||||
static int
|
||||
pytime_divmod(const _PyTime_t t, const _PyTime_t k,
|
||||
_PyTime_t *pq, _PyTime_t *pr)
|
||||
|
@ -768,8 +768,8 @@ pytime_divmod(const _PyTime_t t, const _PyTime_t k,
|
|||
_PyTime_t q = t / k;
|
||||
_PyTime_t r = t % k;
|
||||
if (r < 0) {
|
||||
if (q == _PyTime_MIN) {
|
||||
*pq = _PyTime_MIN;
|
||||
if (q == PyTime_MIN) {
|
||||
*pq = PyTime_MIN;
|
||||
*pr = 0;
|
||||
return -1;
|
||||
}
|
||||
|
@ -784,13 +784,6 @@ pytime_divmod(const _PyTime_t t, const _PyTime_t k,
|
|||
}
|
||||
|
||||
|
||||
_PyTime_t
|
||||
_PyTime_AsNanoseconds(_PyTime_t t)
|
||||
{
|
||||
return pytime_as_nanoseconds(t);
|
||||
}
|
||||
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
_PyTime_t
|
||||
_PyTime_As100Nanoseconds(_PyTime_t t, _PyTime_round_t round)
|
||||
|
@ -926,6 +919,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
|
|||
#endif
|
||||
|
||||
|
||||
// N.B. If raise_exc=0, this may be called without the GIL.
|
||||
static int
|
||||
py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||
{
|
||||
|
@ -1050,6 +1044,18 @@ _PyTime_GetSystemClock(void)
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
PyTime_Time(PyTime_t *result)
|
||||
{
|
||||
if (py_get_system_clock(result, NULL, 1) < 0) {
|
||||
// If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails:
|
||||
// silently ignore the failure and return 0.
|
||||
*result = 0;
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
_PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
||||
{
|
||||
|
@ -1092,6 +1098,7 @@ py_mach_timebase_info(_PyTimeFraction *base, int raise)
|
|||
#endif
|
||||
|
||||
|
||||
// N.B. If raise_exc=0, this may be called without the GIL.
|
||||
static int
|
||||
py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||
{
|
||||
|
@ -1102,13 +1109,13 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
|||
static_assert(sizeof(ticks) <= sizeof(_PyTime_t),
|
||||
"ULONGLONG is larger than _PyTime_t");
|
||||
_PyTime_t t;
|
||||
if (ticks <= (ULONGLONG)_PyTime_MAX) {
|
||||
if (ticks <= (ULONGLONG)PyTime_MAX) {
|
||||
t = (_PyTime_t)ticks;
|
||||
}
|
||||
else {
|
||||
// GetTickCount64() maximum is larger than _PyTime_t maximum:
|
||||
// ULONGLONG is unsigned, whereas _PyTime_t is signed.
|
||||
t = _PyTime_MAX;
|
||||
t = PyTime_MAX;
|
||||
}
|
||||
|
||||
int res = pytime_mul(&t, MS_TO_NS);
|
||||
|
@ -1151,7 +1158,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
|||
|
||||
uint64_t uticks = mach_absolute_time();
|
||||
// unsigned => signed
|
||||
assert(uticks <= (uint64_t)_PyTime_MAX);
|
||||
assert(uticks <= (uint64_t)PyTime_MAX);
|
||||
_PyTime_t ticks = (_PyTime_t)uticks;
|
||||
|
||||
_PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
|
||||
|
@ -1229,6 +1236,17 @@ _PyTime_GetMonotonicClock(void)
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
PyTime_Monotonic(PyTime_t *result)
|
||||
{
|
||||
if (py_get_monotonic_clock(result, NULL, 1) < 0) {
|
||||
*result = 0;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
||||
{
|
||||
|
@ -1268,6 +1286,7 @@ py_win_perf_counter_frequency(_PyTimeFraction *base, int raise)
|
|||
}
|
||||
|
||||
|
||||
// N.B. If raise_exc=0, this may be called without the GIL.
|
||||
static int
|
||||
py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||
{
|
||||
|
@ -1335,6 +1354,25 @@ _PyTime_GetPerfCounter(void)
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
PyTime_PerfCounter(PyTime_t *result)
|
||||
{
|
||||
int res;
|
||||
#ifdef MS_WINDOWS
|
||||
res = py_get_win_perf_counter(result, NULL, 1);
|
||||
#else
|
||||
res = py_get_monotonic_clock(result, NULL, 1);
|
||||
#endif
|
||||
if (res < 0) {
|
||||
// If py_win_perf_counter_frequency() or py_get_monotonic_clock()
|
||||
// fails: silently ignore the failure and return 0.
|
||||
*result = 0;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_PyTime_localtime(time_t t, struct tm *tm)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue