mirror of
https://github.com/python/cpython
synced 2024-11-05 18:12:54 +00:00
3e2c8d84c6
Drop all hardcoded tests. Instead, reimplement each function in Python, usually using decimal.Decimal for the rounding mode. Add much more values to the dataset. Test various timestamp units from picroseconds to seconds, in integer and float. Enhance also _PyTime_AsSecondsDouble().
728 lines
17 KiB
C
728 lines
17 KiB
C
#include "Python.h"
|
|
#ifdef MS_WINDOWS
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
|
|
#endif
|
|
|
|
/* To millisecond (10^-3) */
|
|
#define SEC_TO_MS 1000
|
|
|
|
/* To microseconds (10^-6) */
|
|
#define MS_TO_US 1000
|
|
#define SEC_TO_US (SEC_TO_MS * MS_TO_US)
|
|
|
|
/* To nanoseconds (10^-9) */
|
|
#define US_TO_NS 1000
|
|
#define MS_TO_NS (MS_TO_US * US_TO_NS)
|
|
#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS)
|
|
|
|
/* Conversion from nanoseconds */
|
|
#define NS_TO_MS (1000 * 1000)
|
|
#define NS_TO_US (1000)
|
|
|
|
static void
|
|
error_time_t_overflow(void)
|
|
{
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
"timestamp out of range for platform time_t");
|
|
}
|
|
|
|
time_t
|
|
_PyLong_AsTime_t(PyObject *obj)
|
|
{
|
|
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
|
PY_LONG_LONG val;
|
|
val = PyLong_AsLongLong(obj);
|
|
#else
|
|
long val;
|
|
assert(sizeof(time_t) <= sizeof(long));
|
|
val = PyLong_AsLong(obj);
|
|
#endif
|
|
if (val == -1 && PyErr_Occurred()) {
|
|
if (PyErr_ExceptionMatches(PyExc_OverflowError))
|
|
error_time_t_overflow();
|
|
return -1;
|
|
}
|
|
return (time_t)val;
|
|
}
|
|
|
|
PyObject *
|
|
_PyLong_FromTime_t(time_t t)
|
|
{
|
|
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
|
return PyLong_FromLongLong((PY_LONG_LONG)t);
|
|
#else
|
|
assert(sizeof(time_t) <= sizeof(long));
|
|
return PyLong_FromLong((long)t);
|
|
#endif
|
|
}
|
|
|
|
/* Round to nearest with ties going to nearest even integer
|
|
(_PyTime_ROUND_HALF_EVEN) */
|
|
static double
|
|
_PyTime_RoundHalfEven(double x)
|
|
{
|
|
double rounded = round(x);
|
|
if (fabs(x-rounded) == 0.5)
|
|
/* halfway case: round to even */
|
|
rounded = 2.0*round(x/2.0);
|
|
return rounded;
|
|
}
|
|
|
|
static double
|
|
_PyTime_Round(double x, _PyTime_round_t round)
|
|
{
|
|
if (round == _PyTime_ROUND_HALF_EVEN)
|
|
return _PyTime_RoundHalfEven(x);
|
|
else if (round == _PyTime_ROUND_CEILING)
|
|
return ceil(x);
|
|
else
|
|
return floor(x);
|
|
}
|
|
|
|
static int
|
|
_PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
|
|
double denominator, _PyTime_round_t round)
|
|
{
|
|
double intpart, err;
|
|
/* volatile avoids optimization changing how numbers are rounded */
|
|
volatile double floatpart;
|
|
|
|
floatpart = modf(d, &intpart);
|
|
|
|
floatpart *= denominator;
|
|
floatpart = _PyTime_Round(floatpart, round);
|
|
if (floatpart >= denominator) {
|
|
floatpart -= denominator;
|
|
intpart += 1.0;
|
|
}
|
|
else if (floatpart < 0) {
|
|
floatpart += denominator;
|
|
intpart -= 1.0;
|
|
}
|
|
assert(0.0 <= floatpart && floatpart < denominator);
|
|
|
|
*sec = (time_t)intpart;
|
|
*numerator = (long)floatpart;
|
|
|
|
err = intpart - (double)*sec;
|
|
if (err <= -1.0 || err >= 1.0) {
|
|
error_time_t_overflow();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
|
|
double denominator, _PyTime_round_t round)
|
|
{
|
|
assert(denominator <= (double)LONG_MAX);
|
|
|
|
if (PyFloat_Check(obj)) {
|
|
double d = PyFloat_AsDouble(obj);
|
|
return _PyTime_DoubleToDenominator(d, sec, numerator,
|
|
denominator, round);
|
|
}
|
|
else {
|
|
*sec = _PyLong_AsTime_t(obj);
|
|
*numerator = 0;
|
|
if (*sec == (time_t)-1 && PyErr_Occurred())
|
|
return -1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
|
|
{
|
|
if (PyFloat_Check(obj)) {
|
|
double intpart, err;
|
|
/* volatile avoids optimization changing how numbers are rounded */
|
|
volatile double d;
|
|
|
|
d = PyFloat_AsDouble(obj);
|
|
d = _PyTime_Round(d, round);
|
|
(void)modf(d, &intpart);
|
|
|
|
*sec = (time_t)intpart;
|
|
err = intpart - (double)*sec;
|
|
if (err <= -1.0 || err >= 1.0) {
|
|
error_time_t_overflow();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
else {
|
|
*sec = _PyLong_AsTime_t(obj);
|
|
if (*sec == (time_t)-1 && PyErr_Occurred())
|
|
return -1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec,
|
|
_PyTime_round_t round)
|
|
{
|
|
int res;
|
|
res = _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round);
|
|
assert(0 <= *nsec && *nsec < SEC_TO_NS);
|
|
return res;
|
|
}
|
|
|
|
int
|
|
_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
|
|
_PyTime_round_t round)
|
|
{
|
|
int res;
|
|
res = _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
|
|
assert(0 <= *usec && *usec < SEC_TO_US);
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
_PyTime_overflow(void)
|
|
{
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
"timestamp too large to convert to C _PyTime_t");
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_FromSeconds(int seconds)
|
|
{
|
|
_PyTime_t t;
|
|
t = (_PyTime_t)seconds;
|
|
/* ensure that integer overflow cannot happen, int type should have 32
|
|
bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_MS takes 30
|
|
bits). */
|
|
assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS)
|
|
|| (t < 0 && t >= _PyTime_MIN / SEC_TO_NS));
|
|
t *= SEC_TO_NS;
|
|
return t;
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_FromNanoseconds(PY_LONG_LONG ns)
|
|
{
|
|
_PyTime_t t;
|
|
assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t));
|
|
t = Py_SAFE_DOWNCAST(ns, PY_LONG_LONG, _PyTime_t);
|
|
return t;
|
|
}
|
|
|
|
#ifdef HAVE_CLOCK_GETTIME
|
|
static int
|
|
_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts, int raise)
|
|
{
|
|
_PyTime_t t;
|
|
int res = 0;
|
|
|
|
t = (_PyTime_t)ts->tv_sec * SEC_TO_NS;
|
|
if (t / SEC_TO_NS != ts->tv_sec) {
|
|
if (raise)
|
|
_PyTime_overflow();
|
|
res = -1;
|
|
}
|
|
|
|
t += ts->tv_nsec;
|
|
|
|
*tp = t;
|
|
return res;
|
|
}
|
|
#elif !defined(MS_WINDOWS)
|
|
static int
|
|
_PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
|
|
{
|
|
_PyTime_t t;
|
|
int res = 0;
|
|
|
|
t = (_PyTime_t)tv->tv_sec * SEC_TO_NS;
|
|
if (t / SEC_TO_NS != tv->tv_sec) {
|
|
if (raise)
|
|
_PyTime_overflow();
|
|
res = -1;
|
|
}
|
|
|
|
t += (_PyTime_t)tv->tv_usec * US_TO_NS;
|
|
|
|
*tp = t;
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
|
|
long unit_to_ns)
|
|
{
|
|
double err;
|
|
/* volatile avoids optimization changing how numbers are rounded */
|
|
volatile double d;
|
|
|
|
/* convert to a number of nanoseconds */
|
|
d = value;
|
|
d *= (double)unit_to_ns;
|
|
d = _PyTime_Round(d, round);
|
|
|
|
*t = (_PyTime_t)d;
|
|
err = d - (double)*t;
|
|
if (fabs(err) >= 1.0) {
|
|
_PyTime_overflow();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round,
|
|
long unit_to_ns)
|
|
{
|
|
if (PyFloat_Check(obj)) {
|
|
double d;
|
|
d = PyFloat_AsDouble(obj);
|
|
return _PyTime_FromFloatObject(t, d, round, unit_to_ns);
|
|
}
|
|
else {
|
|
#ifdef HAVE_LONG_LONG
|
|
PY_LONG_LONG sec;
|
|
sec = PyLong_AsLongLong(obj);
|
|
assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t));
|
|
#else
|
|
long sec;
|
|
sec = PyLong_AsLong(obj);
|
|
assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t));
|
|
#endif
|
|
if (sec == -1 && PyErr_Occurred()) {
|
|
if (PyErr_ExceptionMatches(PyExc_OverflowError))
|
|
_PyTime_overflow();
|
|
return -1;
|
|
}
|
|
*t = sec * unit_to_ns;
|
|
if (*t / unit_to_ns != sec) {
|
|
_PyTime_overflow();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
_PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_FromObject(t, obj, round, SEC_TO_NS);
|
|
}
|
|
|
|
int
|
|
_PyTime_FromMillisecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_FromObject(t, obj, round, MS_TO_NS);
|
|
}
|
|
|
|
double
|
|
_PyTime_AsSecondsDouble(_PyTime_t t)
|
|
{
|
|
if (t % SEC_TO_NS == 0) {
|
|
_PyTime_t secs;
|
|
/* Divide using integers to avoid rounding issues on the integer part.
|
|
1e-9 cannot be stored exactly in IEEE 64-bit. */
|
|
secs = t / SEC_TO_NS;
|
|
return (double)secs;
|
|
}
|
|
else {
|
|
return (double)t / 1e9;
|
|
}
|
|
}
|
|
|
|
PyObject *
|
|
_PyTime_AsNanosecondsObject(_PyTime_t t)
|
|
{
|
|
#ifdef HAVE_LONG_LONG
|
|
assert(sizeof(PY_LONG_LONG) >= sizeof(_PyTime_t));
|
|
return PyLong_FromLongLong((PY_LONG_LONG)t);
|
|
#else
|
|
assert(sizeof(long) >= sizeof(_PyTime_t));
|
|
return PyLong_FromLong((long)t);
|
|
#endif
|
|
}
|
|
|
|
static _PyTime_t
|
|
_PyTime_Divide(const _PyTime_t t, const _PyTime_t k,
|
|
const _PyTime_round_t round)
|
|
{
|
|
assert(k > 1);
|
|
if (round == _PyTime_ROUND_HALF_EVEN) {
|
|
_PyTime_t x, r, abs_r;
|
|
x = t / k;
|
|
r = t % k;
|
|
abs_r = Py_ABS(r);
|
|
if (abs_r > k / 2 || (abs_r == k / 2 && (Py_ABS(x) & 1))) {
|
|
if (t >= 0)
|
|
x++;
|
|
else
|
|
x--;
|
|
}
|
|
return x;
|
|
}
|
|
else if (round == _PyTime_ROUND_CEILING) {
|
|
if (t >= 0)
|
|
return (t + k - 1) / k;
|
|
else
|
|
return t / k;
|
|
}
|
|
else {
|
|
if (t >= 0)
|
|
return t / k;
|
|
else
|
|
return (t - (k - 1)) / k;
|
|
}
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_Divide(t, NS_TO_MS, round);
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_Divide(t, NS_TO_US, round);
|
|
}
|
|
|
|
static int
|
|
_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round,
|
|
int raise)
|
|
{
|
|
_PyTime_t secs, ns;
|
|
int res = 0;
|
|
int usec;
|
|
|
|
secs = t / SEC_TO_NS;
|
|
ns = t % SEC_TO_NS;
|
|
|
|
#ifdef MS_WINDOWS
|
|
/* On Windows, timeval.tv_sec is a long (32 bit),
|
|
whereas time_t can be 64-bit. */
|
|
assert(sizeof(tv->tv_sec) == sizeof(long));
|
|
#if SIZEOF_TIME_T > SIZEOF_LONG
|
|
if (secs > LONG_MAX) {
|
|
secs = LONG_MAX;
|
|
res = -1;
|
|
}
|
|
else if (secs < LONG_MIN) {
|
|
secs = LONG_MIN;
|
|
res = -1;
|
|
}
|
|
#endif
|
|
tv->tv_sec = (long)secs;
|
|
#else
|
|
/* On OpenBSD 5.4, timeval.tv_sec is a long.
|
|
Example: long is 64-bit, whereas time_t is 32-bit. */
|
|
tv->tv_sec = secs;
|
|
if ((_PyTime_t)tv->tv_sec != secs)
|
|
res = -1;
|
|
#endif
|
|
|
|
usec = (int)_PyTime_Divide(ns, US_TO_NS, round);
|
|
if (usec < 0) {
|
|
usec += SEC_TO_US;
|
|
tv->tv_sec -= 1;
|
|
}
|
|
else if (usec >= SEC_TO_US) {
|
|
usec -= SEC_TO_US;
|
|
tv->tv_sec += 1;
|
|
}
|
|
|
|
assert(0 <= usec && usec < SEC_TO_US);
|
|
tv->tv_usec = usec;
|
|
|
|
if (res && raise)
|
|
_PyTime_overflow();
|
|
return res;
|
|
}
|
|
|
|
int
|
|
_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_AsTimeval_impl(t, tv, round, 1);
|
|
}
|
|
|
|
int
|
|
_PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
|
|
{
|
|
return _PyTime_AsTimeval_impl(t, tv, round, 0);
|
|
}
|
|
|
|
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
|
|
int
|
|
_PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
|
|
{
|
|
_PyTime_t secs, nsec;
|
|
|
|
secs = t / SEC_TO_NS;
|
|
nsec = t % SEC_TO_NS;
|
|
if (nsec < 0) {
|
|
nsec += SEC_TO_NS;
|
|
secs -= 1;
|
|
}
|
|
ts->tv_sec = (time_t)secs;
|
|
assert(0 <= nsec && nsec < SEC_TO_NS);
|
|
ts->tv_nsec = nsec;
|
|
|
|
if ((_PyTime_t)ts->tv_sec != secs) {
|
|
_PyTime_overflow();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
|
{
|
|
#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;
|
|
/* 11,644,473,600,000,000,000: number of nanoseconds between
|
|
the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
|
|
days). */
|
|
*tp = large.QuadPart * 100 - 11644473600000000000;
|
|
if (info) {
|
|
DWORD timeAdjustment, timeIncrement;
|
|
BOOL isTimeAdjustmentDisabled, ok;
|
|
|
|
info->implementation = "GetSystemTimeAsFileTime()";
|
|
info->monotonic = 0;
|
|
ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
|
|
&isTimeAdjustmentDisabled);
|
|
if (!ok) {
|
|
PyErr_SetFromWindowsErr(0);
|
|
return -1;
|
|
}
|
|
info->resolution = timeIncrement * 1e-7;
|
|
info->adjustable = 1;
|
|
}
|
|
|
|
#else /* MS_WINDOWS */
|
|
int err;
|
|
#ifdef HAVE_CLOCK_GETTIME
|
|
struct timespec ts;
|
|
#else
|
|
struct timeval tv;
|
|
#endif
|
|
|
|
assert(info == NULL || raise);
|
|
|
|
#ifdef HAVE_CLOCK_GETTIME
|
|
err = clock_gettime(CLOCK_REALTIME, &ts);
|
|
if (err) {
|
|
if (raise)
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
if (_PyTime_FromTimespec(tp, &ts, raise) < 0)
|
|
return -1;
|
|
|
|
if (info) {
|
|
struct timespec res;
|
|
info->implementation = "clock_gettime(CLOCK_REALTIME)";
|
|
info->monotonic = 0;
|
|
info->adjustable = 1;
|
|
if (clock_getres(CLOCK_REALTIME, &res) == 0)
|
|
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
|
|
else
|
|
info->resolution = 1e-9;
|
|
}
|
|
#else /* HAVE_CLOCK_GETTIME */
|
|
|
|
/* test gettimeofday() */
|
|
#ifdef GETTIMEOFDAY_NO_TZ
|
|
err = gettimeofday(&tv);
|
|
#else
|
|
err = gettimeofday(&tv, (struct timezone *)NULL);
|
|
#endif
|
|
if (err) {
|
|
if (raise)
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
if (_PyTime_FromTimeval(tp, &tv, raise) < 0)
|
|
return -1;
|
|
|
|
if (info) {
|
|
info->implementation = "gettimeofday()";
|
|
info->resolution = 1e-6;
|
|
info->monotonic = 0;
|
|
info->adjustable = 1;
|
|
}
|
|
#endif /* !HAVE_CLOCK_GETTIME */
|
|
#endif /* !MS_WINDOWS */
|
|
return 0;
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_GetSystemClock(void)
|
|
{
|
|
_PyTime_t t;
|
|
if (pygettimeofday_new(&t, NULL, 0) < 0) {
|
|
/* should not happen, _PyTime_Init() checked the clock at startup */
|
|
assert(0);
|
|
|
|
/* use a fixed value instead of a random value from the stack */
|
|
t = 0;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
int
|
|
_PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
|
{
|
|
return pygettimeofday_new(t, info, 1);
|
|
}
|
|
|
|
|
|
static int
|
|
pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
|
{
|
|
#if defined(MS_WINDOWS)
|
|
ULONGLONG result;
|
|
|
|
assert(info == NULL || raise);
|
|
|
|
result = GetTickCount64();
|
|
|
|
*tp = result * MS_TO_NS;
|
|
if (*tp / MS_TO_NS != result) {
|
|
if (raise) {
|
|
_PyTime_overflow();
|
|
return -1;
|
|
}
|
|
/* Hello, time traveler! */
|
|
assert(0);
|
|
}
|
|
|
|
if (info) {
|
|
DWORD timeAdjustment, timeIncrement;
|
|
BOOL isTimeAdjustmentDisabled, ok;
|
|
info->implementation = "GetTickCount64()";
|
|
info->monotonic = 1;
|
|
ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
|
|
&isTimeAdjustmentDisabled);
|
|
if (!ok) {
|
|
PyErr_SetFromWindowsErr(0);
|
|
return -1;
|
|
}
|
|
info->resolution = timeIncrement * 1e-7;
|
|
info->adjustable = 0;
|
|
}
|
|
|
|
#elif defined(__APPLE__)
|
|
static mach_timebase_info_data_t timebase;
|
|
uint64_t time;
|
|
|
|
if (timebase.denom == 0) {
|
|
/* According to the Technical Q&A QA1398, mach_timebase_info() cannot
|
|
fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
|
|
(void)mach_timebase_info(&timebase);
|
|
}
|
|
|
|
time = mach_absolute_time();
|
|
|
|
/* apply timebase factor */
|
|
time *= timebase.numer;
|
|
time /= timebase.denom;
|
|
|
|
*tp = time;
|
|
|
|
if (info) {
|
|
info->implementation = "mach_absolute_time()";
|
|
info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
|
|
info->monotonic = 1;
|
|
info->adjustable = 0;
|
|
}
|
|
|
|
#else
|
|
struct timespec ts;
|
|
#ifdef CLOCK_HIGHRES
|
|
const clockid_t clk_id = CLOCK_HIGHRES;
|
|
const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
|
|
#else
|
|
const clockid_t clk_id = CLOCK_MONOTONIC;
|
|
const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
|
|
#endif
|
|
|
|
assert(info == NULL || raise);
|
|
|
|
if (clock_gettime(clk_id, &ts) != 0) {
|
|
if (raise) {
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (info) {
|
|
struct timespec res;
|
|
info->monotonic = 1;
|
|
info->implementation = implementation;
|
|
info->adjustable = 0;
|
|
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;
|
|
}
|
|
|
|
_PyTime_t
|
|
_PyTime_GetMonotonicClock(void)
|
|
{
|
|
_PyTime_t t;
|
|
if (pymonotonic(&t, NULL, 0) < 0) {
|
|
/* should not happen, _PyTime_Init() checked that monotonic clock at
|
|
startup */
|
|
assert(0);
|
|
|
|
/* use a fixed value instead of a random value from the stack */
|
|
t = 0;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
int
|
|
_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
|
{
|
|
return pymonotonic(tp, info, 1);
|
|
}
|
|
|
|
int
|
|
_PyTime_Init(void)
|
|
{
|
|
_PyTime_t t;
|
|
|
|
/* ensure that the system clock works */
|
|
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
|
|
return -1;
|
|
|
|
/* ensure that the operating system provides a monotonic clock */
|
|
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
|
|
return -1;
|
|
|
|
/* check that _PyTime_FromSeconds() cannot overflow */
|
|
assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS);
|
|
assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS);
|
|
return 0;
|
|
}
|