bpo-37205: time.time() cannot fail with fatal error (GH-23314)
time.time(), time.perf_counter() and time.monotonic() functions can no longer fail with a Python fatal error, instead raise a regular Python exception on failure. Remove _PyTime_Init(): don't check system, monotonic and perf counter clocks at startup anymore. On error, _PyTime_GetSystemClock(), _PyTime_GetMonotonicClock() and _PyTime_GetPerfCounter() now silently ignore the error and return 0. They cannot fail with a Python fatal error anymore. Add py_mach_timebase_info() and win_perf_counter_frequency() sub-functions.
This commit is contained in:
parent
5909a494cd
commit
ae6cd7cfda
|
@ -164,22 +164,6 @@ PyAPI_FUNC(_PyTime_t) _PyTime_MulDiv(_PyTime_t ticks,
|
||||||
_PyTime_t mul,
|
_PyTime_t mul,
|
||||||
_PyTime_t div);
|
_PyTime_t div);
|
||||||
|
|
||||||
/* Get the current time from the system clock.
|
|
||||||
|
|
||||||
The function cannot fail. _PyTime_Init() ensures that the system clock
|
|
||||||
works. */
|
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
|
|
||||||
|
|
||||||
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
|
||||||
The clock is not affected by system clock updates. The reference point of
|
|
||||||
the returned value is undefined, so that only the difference between the
|
|
||||||
results of consecutive calls is valid.
|
|
||||||
|
|
||||||
The function cannot fail. _PyTime_Init() ensures that a monotonic clock
|
|
||||||
is available and works. */
|
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
|
|
||||||
|
|
||||||
|
|
||||||
/* Structure used by time.get_clock_info() */
|
/* Structure used by time.get_clock_info() */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *implementation;
|
const char *implementation;
|
||||||
|
@ -189,13 +173,34 @@ typedef struct {
|
||||||
} _Py_clock_info_t;
|
} _Py_clock_info_t;
|
||||||
|
|
||||||
/* Get the current time from the system clock.
|
/* Get the current time from the system clock.
|
||||||
* Fill clock information if info is not NULL.
|
|
||||||
* Raise an exception and return -1 on error, return 0 on success.
|
If the internal clock fails, silently ignore the error and return 0.
|
||||||
|
On integer overflow, silently ignore the overflow and truncated the clock to
|
||||||
|
_PyTime_MIN or _PyTime_MAX.
|
||||||
|
|
||||||
|
Use _PyTime_GetSystemClockWithInfo() to check for failure. */
|
||||||
|
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
|
||||||
|
|
||||||
|
/* Get the current time from the system clock.
|
||||||
|
* On success, set *t and *info (if not NULL), and return 0.
|
||||||
|
* On error, raise an exception and return -1.
|
||||||
*/
|
*/
|
||||||
PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
|
PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo(
|
||||||
_PyTime_t *t,
|
_PyTime_t *t,
|
||||||
_Py_clock_info_t *info);
|
_Py_clock_info_t *info);
|
||||||
|
|
||||||
|
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
||||||
|
The clock is not affected by system clock updates. The reference point of
|
||||||
|
the returned value is undefined, so that only the difference between the
|
||||||
|
results of consecutive calls is valid.
|
||||||
|
|
||||||
|
If the internal clock fails, silently ignore the error and return 0.
|
||||||
|
On integer overflow, silently ignore the overflow and truncated the clock to
|
||||||
|
_PyTime_MIN or _PyTime_MAX.
|
||||||
|
|
||||||
|
Use _PyTime_GetMonotonicClockWithInfo() to check for failure. */
|
||||||
|
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
|
||||||
|
|
||||||
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
||||||
The clock is not affected by system clock updates. The reference point of
|
The clock is not affected by system clock updates. The reference point of
|
||||||
the returned value is undefined, so that only the difference between the
|
the returned value is undefined, so that only the difference between the
|
||||||
|
@ -209,10 +214,6 @@ PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo(
|
||||||
_Py_clock_info_t *info);
|
_Py_clock_info_t *info);
|
||||||
|
|
||||||
|
|
||||||
/* Initialize time.
|
|
||||||
Return 0 on success, raise an exception and return -1 on error. */
|
|
||||||
PyAPI_FUNC(int) _PyTime_Init(void);
|
|
||||||
|
|
||||||
/* Converts a timestamp to the Gregorian time, using the local time zone.
|
/* Converts a timestamp to the Gregorian time, using the local time zone.
|
||||||
Return 0 on success, raise an exception and return -1 on error. */
|
Return 0 on success, raise an exception and return -1 on error. */
|
||||||
PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
|
PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
|
||||||
|
@ -224,8 +225,11 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
|
||||||
/* Get the performance counter: clock with the highest available resolution to
|
/* Get the performance counter: clock with the highest available resolution to
|
||||||
measure a short duration.
|
measure a short duration.
|
||||||
|
|
||||||
The function cannot fail. _PyTime_Init() ensures that the system clock
|
If the internal clock fails, silently ignore the error and return 0.
|
||||||
works. */
|
On integer overflow, silently ignore the overflow and truncated the clock to
|
||||||
|
_PyTime_MIN or _PyTime_MAX.
|
||||||
|
|
||||||
|
Use _PyTime_GetPerfCounterWithInfo() to check for failure. */
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
|
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
|
||||||
|
|
||||||
/* Get the performance counter: clock with the highest available resolution to
|
/* Get the performance counter: clock with the highest available resolution to
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:func:`time.time()`, :func:`time.perf_counter()` and
|
||||||
|
:func:`time.monotonic()` functions can no longer fail with a Python fatal
|
||||||
|
error, instead raise a regular Python exception on failure.
|
|
@ -74,10 +74,21 @@ _PyFloat_FromPyTime(_PyTime_t t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_system_time(_PyTime_t *t)
|
||||||
|
{
|
||||||
|
// Avoid _PyTime_GetSystemClock() which silently ignores errors.
|
||||||
|
return _PyTime_GetSystemClockWithInfo(t, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_time(PyObject *self, PyObject *unused)
|
time_time(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
_PyTime_t t = _PyTime_GetSystemClock();
|
_PyTime_t t;
|
||||||
|
if (get_system_time(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return _PyFloat_FromPyTime(t);
|
return _PyFloat_FromPyTime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +102,10 @@ Fractions of a second may be present if the system clock provides them.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_time_ns(PyObject *self, PyObject *unused)
|
time_time_ns(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
_PyTime_t t = _PyTime_GetSystemClock();
|
_PyTime_t t;
|
||||||
|
if (get_system_time(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return _PyTime_AsNanosecondsObject(t);
|
return _PyTime_AsNanosecondsObject(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,15 +161,6 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_CLOCK */
|
#endif /* HAVE_CLOCK */
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
perf_counter(_Py_clock_info_t *info)
|
|
||||||
{
|
|
||||||
_PyTime_t t;
|
|
||||||
if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return _PyFloat_FromPyTime(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
#ifdef HAVE_CLOCK_GETTIME
|
||||||
|
|
||||||
|
@ -1096,10 +1101,22 @@ the local timezone used by methods such as localtime, but this behaviour\n\
|
||||||
should not be relied on.");
|
should not be relied on.");
|
||||||
#endif /* HAVE_WORKING_TZSET */
|
#endif /* HAVE_WORKING_TZSET */
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_monotonic(_PyTime_t *t)
|
||||||
|
{
|
||||||
|
// Avoid _PyTime_GetMonotonicClock() which silently ignores errors.
|
||||||
|
return _PyTime_GetMonotonicClockWithInfo(t, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_monotonic(PyObject *self, PyObject *unused)
|
time_monotonic(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
_PyTime_t t = _PyTime_GetMonotonicClock();
|
_PyTime_t t;
|
||||||
|
if (get_monotonic(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return _PyFloat_FromPyTime(t);
|
return _PyFloat_FromPyTime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,7 +1128,10 @@ Monotonic clock, cannot go backward.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_monotonic_ns(PyObject *self, PyObject *unused)
|
time_monotonic_ns(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
_PyTime_t t = _PyTime_GetMonotonicClock();
|
_PyTime_t t;
|
||||||
|
if (get_monotonic(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return _PyTime_AsNanosecondsObject(t);
|
return _PyTime_AsNanosecondsObject(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,10 +1140,23 @@ PyDoc_STRVAR(monotonic_ns_doc,
|
||||||
\n\
|
\n\
|
||||||
Monotonic clock, cannot go backward, as nanoseconds.");
|
Monotonic clock, cannot go backward, as nanoseconds.");
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_perf_counter(_PyTime_t *t)
|
||||||
|
{
|
||||||
|
// Avoid _PyTime_GetPerfCounter() which silently ignores errors.
|
||||||
|
return _PyTime_GetPerfCounterWithInfo(t, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_perf_counter(PyObject *self, PyObject *unused)
|
time_perf_counter(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
return perf_counter(NULL);
|
_PyTime_t t;
|
||||||
|
if (get_perf_counter(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return _PyFloat_FromPyTime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(perf_counter_doc,
|
PyDoc_STRVAR(perf_counter_doc,
|
||||||
|
@ -1131,10 +1164,14 @@ PyDoc_STRVAR(perf_counter_doc,
|
||||||
\n\
|
\n\
|
||||||
Performance counter for benchmarking.");
|
Performance counter for benchmarking.");
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_perf_counter_ns(PyObject *self, PyObject *unused)
|
time_perf_counter_ns(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
_PyTime_t t = _PyTime_GetPerfCounter();
|
_PyTime_t t;
|
||||||
|
if (get_perf_counter(&t) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return _PyTime_AsNanosecondsObject(t);
|
return _PyTime_AsNanosecondsObject(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2025,7 +2062,10 @@ pysleep(_PyTime_t secs)
|
||||||
HANDLE hInterruptEvent;
|
HANDLE hInterruptEvent;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
deadline = _PyTime_GetMonotonicClock() + secs;
|
if (get_monotonic(&monotonic) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
deadline = monotonic + secs;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
|
@ -2077,10 +2117,13 @@ pysleep(_PyTime_t secs)
|
||||||
if (PyErr_CheckSignals())
|
if (PyErr_CheckSignals())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
monotonic = _PyTime_GetMonotonicClock();
|
if (get_monotonic(&monotonic) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
secs = deadline - monotonic;
|
secs = deadline - monotonic;
|
||||||
if (secs < 0)
|
if (secs < 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
/* retry with the recomputed delay */
|
/* retry with the recomputed delay */
|
||||||
} while (1);
|
} while (1);
|
||||||
|
|
||||||
|
|
|
@ -763,12 +763,6 @@ pycore_interp_init(PyThreadState *tstate)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_Py_IsMainInterpreter(tstate)) {
|
|
||||||
if (_PyTime_Init() < 0) {
|
|
||||||
return _PyStatus_ERR("can't initialize time");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status = _PySys_Create(tstate, &sysmod);
|
status = _PySys_Create(tstate, &sysmod);
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
goto done;
|
goto done;
|
||||||
|
|
238
Python/pytime.c
238
Python/pytime.c
|
@ -304,8 +304,8 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise)
|
||||||
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
|
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
|
||||||
if (raise) {
|
if (raise) {
|
||||||
_PyTime_overflow();
|
_PyTime_overflow();
|
||||||
|
res = -1;
|
||||||
}
|
}
|
||||||
res = -1;
|
|
||||||
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
|
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -318,8 +318,8 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise)
|
||||||
if (t > _PyTime_MAX - nsec) {
|
if (t > _PyTime_MAX - nsec) {
|
||||||
if (raise) {
|
if (raise) {
|
||||||
_PyTime_overflow();
|
_PyTime_overflow();
|
||||||
|
res = -1;
|
||||||
}
|
}
|
||||||
res = -1;
|
|
||||||
t = _PyTime_MAX;
|
t = _PyTime_MAX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -350,8 +350,8 @@ pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise)
|
||||||
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
|
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
|
||||||
if (raise) {
|
if (raise) {
|
||||||
_PyTime_overflow();
|
_PyTime_overflow();
|
||||||
|
res = -1;
|
||||||
}
|
}
|
||||||
res = -1;
|
|
||||||
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
|
t = (t > 0) ? _PyTime_MAX : _PyTime_MIN;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -364,8 +364,8 @@ pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise)
|
||||||
if (t > _PyTime_MAX - usec) {
|
if (t > _PyTime_MAX - usec) {
|
||||||
if (raise) {
|
if (raise) {
|
||||||
_PyTime_overflow();
|
_PyTime_overflow();
|
||||||
|
res = -1;
|
||||||
}
|
}
|
||||||
res = -1;
|
|
||||||
t = _PyTime_MAX;
|
t = _PyTime_MAX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -656,7 +656,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
||||||
{
|
{
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
FILETIME system_time;
|
FILETIME system_time;
|
||||||
|
@ -769,9 +769,10 @@ _PyTime_t
|
||||||
_PyTime_GetSystemClock(void)
|
_PyTime_GetSystemClock(void)
|
||||||
{
|
{
|
||||||
_PyTime_t t;
|
_PyTime_t t;
|
||||||
if (pygettimeofday(&t, NULL, 0) < 0) {
|
if (py_get_system_clock(&t, NULL, 0) < 0) {
|
||||||
/* should not happen, _PyTime_Init() checked the clock at startup */
|
// If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails:
|
||||||
Py_FatalError("pygettimeofday() failed");
|
// silently ignore the failure and return 0.
|
||||||
|
t = 0;
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -779,11 +780,61 @@ _PyTime_GetSystemClock(void)
|
||||||
int
|
int
|
||||||
_PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
_PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
return pygettimeofday(t, info, 1);
|
return py_get_system_clock(t, info, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
static int
|
static int
|
||||||
pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise)
|
||||||
|
{
|
||||||
|
static mach_timebase_info_data_t timebase;
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Sanity check: should never occur in practice */
|
||||||
|
if (timebase.numer < 1 || timebase.denom < 1) {
|
||||||
|
if (raise) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
|
"invalid mach_timebase_info");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that timebase.numer and timebase.denom can be casted to
|
||||||
|
_PyTime_t. In practice, timebase uses uint32_t, so casting cannot
|
||||||
|
overflow. At the end, only make sure that the type is uint32_t
|
||||||
|
(_PyTime_t is 64-bit long). */
|
||||||
|
Py_BUILD_ASSERT(sizeof(timebase.numer) < sizeof(_PyTime_t));
|
||||||
|
Py_BUILD_ASSERT(sizeof(timebase.denom) < sizeof(_PyTime_t));
|
||||||
|
|
||||||
|
/* Make sure that (ticks * timebase.numer) cannot overflow in
|
||||||
|
_PyTime_MulDiv(), with ticks < timebase.denom.
|
||||||
|
|
||||||
|
Known time bases:
|
||||||
|
|
||||||
|
* always (1, 1) on Intel
|
||||||
|
* (1000000000, 33333335) or (1000000000, 25000000) on PowerPC
|
||||||
|
|
||||||
|
None of these time bases can overflow with 64-bit _PyTime_t, but
|
||||||
|
check for overflow, just in case. */
|
||||||
|
if ((_PyTime_t)timebase.numer > _PyTime_MAX / (_PyTime_t)timebase.denom) {
|
||||||
|
if (raise) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
|
"mach_timebase_info is too large");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pnumer = (_PyTime_t)timebase.numer;
|
||||||
|
*pdenom = (_PyTime_t)timebase.denom;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
||||||
{
|
{
|
||||||
#if defined(MS_WINDOWS)
|
#if defined(MS_WINDOWS)
|
||||||
ULONGLONG ticks;
|
ULONGLONG ticks;
|
||||||
|
@ -800,10 +851,12 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
||||||
_PyTime_overflow();
|
_PyTime_overflow();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Hello, time traveler! */
|
// Truncate to _PyTime_MAX silently.
|
||||||
Py_FatalError("pymonotonic: integer overflow");
|
*tp = _PyTime_MAX;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*tp = t * MS_TO_NS;
|
||||||
}
|
}
|
||||||
*tp = t * MS_TO_NS;
|
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
DWORD timeAdjustment, timeIncrement;
|
DWORD timeAdjustment, timeIncrement;
|
||||||
|
@ -821,56 +874,23 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
static mach_timebase_info_data_t timebase;
|
static _PyTime_t timebase_numer = 0;
|
||||||
uint64_t ticks;
|
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) {
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Sanity check: should never occur in practice */
|
|
||||||
if (timebase.numer < 1 || timebase.denom < 1) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
|
||||||
"invalid mach_timebase_info");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that timebase.numer and timebase.denom can be casted to
|
|
||||||
_PyTime_t. In practice, timebase uses uint32_t, so casting cannot
|
|
||||||
overflow. At the end, only make sure that the type is uint32_t
|
|
||||||
(_PyTime_t is 64-bit long). */
|
|
||||||
assert(sizeof(timebase.numer) < sizeof(_PyTime_t));
|
|
||||||
assert(sizeof(timebase.denom) < sizeof(_PyTime_t));
|
|
||||||
|
|
||||||
/* Make sure that (ticks * timebase.numer) cannot overflow in
|
|
||||||
_PyTime_MulDiv(), with ticks < timebase.denom.
|
|
||||||
|
|
||||||
Known time bases:
|
|
||||||
|
|
||||||
* always (1, 1) on Intel
|
|
||||||
* (1000000000, 33333335) or (1000000000, 25000000) on PowerPC
|
|
||||||
|
|
||||||
None of these time bases can overflow with 64-bit _PyTime_t, but
|
|
||||||
check for overflow, just in case. */
|
|
||||||
if ((_PyTime_t)timebase.numer > _PyTime_MAX / (_PyTime_t)timebase.denom) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
|
||||||
"mach_timebase_info is too large");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
info->implementation = "mach_absolute_time()";
|
info->implementation = "mach_absolute_time()";
|
||||||
info->resolution = (double)timebase.numer / (double)timebase.denom * 1e-9;
|
info->resolution = (double)timebase_numer / (double)timebase_denom * 1e-9;
|
||||||
info->monotonic = 1;
|
info->monotonic = 1;
|
||||||
info->adjustable = 0;
|
info->adjustable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ticks = mach_absolute_time();
|
uint64_t ticks = mach_absolute_time();
|
||||||
*tp = _PyTime_MulDiv(ticks,
|
*tp = _PyTime_MulDiv((_PyTime_t)ticks, timebase_numer, timebase_denom);
|
||||||
(_PyTime_t)timebase.numer,
|
|
||||||
(_PyTime_t)timebase.denom);
|
|
||||||
|
|
||||||
#elif defined(__hpux)
|
#elif defined(__hpux)
|
||||||
hrtime_t time;
|
hrtime_t time;
|
||||||
|
@ -934,10 +954,10 @@ _PyTime_t
|
||||||
_PyTime_GetMonotonicClock(void)
|
_PyTime_GetMonotonicClock(void)
|
||||||
{
|
{
|
||||||
_PyTime_t t;
|
_PyTime_t t;
|
||||||
if (pymonotonic(&t, NULL, 0) < 0) {
|
if (py_get_monotonic_clock(&t, NULL, 0) < 0) {
|
||||||
/* should not happen, _PyTime_Init() checked that monotonic clock at
|
// If mach_timebase_info(), clock_gettime() or gethrtime() fails:
|
||||||
startup */
|
// silently ignore the failure and return 0.
|
||||||
Py_FatalError("pymonotonic() failed");
|
t = 0;
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -945,54 +965,69 @@ _PyTime_GetMonotonicClock(void)
|
||||||
int
|
int
|
||||||
_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
return pymonotonic(tp, info, 1);
|
return py_get_monotonic_clock(tp, info, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
static int
|
static int
|
||||||
win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info)
|
win_perf_counter_frequency(LONGLONG *pfrequency, int raise)
|
||||||
{
|
{
|
||||||
static LONGLONG frequency = 0;
|
LONGLONG frequency;
|
||||||
LARGE_INTEGER now;
|
|
||||||
LONGLONG ticksll;
|
|
||||||
_PyTime_t ticks;
|
|
||||||
|
|
||||||
if (frequency == 0) {
|
LARGE_INTEGER freq;
|
||||||
LARGE_INTEGER freq;
|
if (!QueryPerformanceFrequency(&freq)) {
|
||||||
if (!QueryPerformanceFrequency(&freq)) {
|
if (raise) {
|
||||||
PyErr_SetFromWindowsErr(0);
|
PyErr_SetFromWindowsErr(0);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
frequency = freq.QuadPart;
|
return -1;
|
||||||
|
}
|
||||||
|
frequency = freq.QuadPart;
|
||||||
|
|
||||||
/* Sanity check: should never occur in practice */
|
/* Sanity check: should never occur in practice */
|
||||||
if (frequency < 1) {
|
if (frequency < 1) {
|
||||||
|
if (raise) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"invalid QueryPerformanceFrequency");
|
"invalid QueryPerformanceFrequency");
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check that frequency can be casted to _PyTime_t.
|
/* Check that frequency can be casted to _PyTime_t.
|
||||||
|
|
||||||
Make also sure that (ticks * SEC_TO_NS) cannot overflow in
|
Make also sure that (ticks * SEC_TO_NS) cannot overflow in
|
||||||
_PyTime_MulDiv(), with ticks < frequency.
|
_PyTime_MulDiv(), with ticks < frequency.
|
||||||
|
|
||||||
Known QueryPerformanceFrequency() values:
|
Known QueryPerformanceFrequency() values:
|
||||||
|
|
||||||
* 10,000,000 (10 MHz): 100 ns resolution
|
* 10,000,000 (10 MHz): 100 ns resolution
|
||||||
* 3,579,545 Hz (3.6 MHz): 279 ns resolution
|
* 3,579,545 Hz (3.6 MHz): 279 ns resolution
|
||||||
|
|
||||||
None of these frequencies can overflow with 64-bit _PyTime_t, but
|
None of these frequencies can overflow with 64-bit _PyTime_t, but
|
||||||
check for overflow, just in case. */
|
check for overflow, just in case. */
|
||||||
if (frequency > _PyTime_MAX
|
if (frequency > _PyTime_MAX
|
||||||
|| frequency > (LONGLONG)_PyTime_MAX / (LONGLONG)SEC_TO_NS) {
|
|| frequency > (LONGLONG)_PyTime_MAX / (LONGLONG)SEC_TO_NS)
|
||||||
|
{
|
||||||
|
if (raise) {
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"QueryPerformanceFrequency is too large");
|
"QueryPerformanceFrequency is too large");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pfrequency = frequency;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
|
||||||
|
{
|
||||||
|
static LONGLONG frequency = 0;
|
||||||
|
if (frequency == 0) {
|
||||||
|
if (win_perf_counter_frequency(&frequency, raise) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryPerformanceCounter(&now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
|
@ -1002,11 +1037,13 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info)
|
||||||
info->adjustable = 0;
|
info->adjustable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LARGE_INTEGER now;
|
||||||
QueryPerformanceCounter(&now);
|
QueryPerformanceCounter(&now);
|
||||||
ticksll = now.QuadPart;
|
LONGLONG ticksll = now.QuadPart;
|
||||||
|
|
||||||
/* Make sure that casting LONGLONG to _PyTime_t cannot overflow,
|
/* Make sure that casting LONGLONG to _PyTime_t cannot overflow,
|
||||||
both types are signed */
|
both types are signed */
|
||||||
|
_PyTime_t ticks;
|
||||||
Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks));
|
Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks));
|
||||||
ticks = (_PyTime_t)ticksll;
|
ticks = (_PyTime_t)ticksll;
|
||||||
|
|
||||||
|
@ -1020,7 +1057,7 @@ int
|
||||||
_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
return win_perf_counter(t, info);
|
return py_get_win_perf_counter(t, info, 1);
|
||||||
#else
|
#else
|
||||||
return _PyTime_GetMonotonicClockWithInfo(t, info);
|
return _PyTime_GetMonotonicClockWithInfo(t, info);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1031,32 +1068,21 @@ _PyTime_t
|
||||||
_PyTime_GetPerfCounter(void)
|
_PyTime_GetPerfCounter(void)
|
||||||
{
|
{
|
||||||
_PyTime_t t;
|
_PyTime_t t;
|
||||||
if (_PyTime_GetPerfCounterWithInfo(&t, NULL)) {
|
int res;
|
||||||
Py_FatalError("_PyTime_GetPerfCounterWithInfo() failed");
|
#ifdef MS_WINDOWS
|
||||||
|
res = py_get_win_perf_counter(&t, NULL, 0);
|
||||||
|
#else
|
||||||
|
res = py_get_monotonic_clock(&t, NULL, 0);
|
||||||
|
#endif
|
||||||
|
if (res < 0) {
|
||||||
|
// If win_perf_counter_frequency() or py_get_monotonic_clock() fails:
|
||||||
|
// silently ignore the failure and return 0.
|
||||||
|
t = 0;
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
_PyTime_Init(void)
|
|
||||||
{
|
|
||||||
/* check that time.time(), time.monotonic() and time.perf_counter() clocks
|
|
||||||
are working properly to not have to check for exceptions at runtime. If
|
|
||||||
a clock works once, it cannot fail in next calls. */
|
|
||||||
_PyTime_t t;
|
|
||||||
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_localtime(time_t t, struct tm *tm)
|
_PyTime_localtime(time_t t, struct tm *tm)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue