From ae6cd7cfdab0599139002c526953d907696d9eef Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Nov 2020 16:08:05 +0100 Subject: 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. --- Include/pytime.h | 52 ++--- .../2020-11-16-15-08-12.bpo-37205.Wh5svI.rst | 3 + Modules/timemodule.c | 87 ++++++-- Python/pylifecycle.c | 6 - Python/pytime.c | 238 ++++++++++++--------- 5 files changed, 228 insertions(+), 158 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-11-16-15-08-12.bpo-37205.Wh5svI.rst diff --git a/Include/pytime.h b/Include/pytime.h index bdda1da..944170f 100644 --- a/Include/pytime.h +++ b/Include/pytime.h @@ -164,22 +164,6 @@ PyAPI_FUNC(_PyTime_t) _PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _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() */ typedef struct { const char *implementation; @@ -189,8 +173,17 @@ typedef struct { } _Py_clock_info_t; /* 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( _PyTime_t *t, @@ -201,6 +194,18 @@ PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo( 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. + 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. + Fill info (if set) with information of the function used to get the time. Return 0 on success, raise an exception and return -1 on error. */ @@ -209,10 +214,6 @@ PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo( _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. Return 0 on success, raise an exception and return -1 on error. */ 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 measure a short duration. - The function cannot fail. _PyTime_Init() ensures that the system clock - works. */ + 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_GetPerfCounterWithInfo() to check for failure. */ PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void); /* Get the performance counter: clock with the highest available resolution to diff --git a/Misc/NEWS.d/next/Library/2020-11-16-15-08-12.bpo-37205.Wh5svI.rst b/Misc/NEWS.d/next/Library/2020-11-16-15-08-12.bpo-37205.Wh5svI.rst new file mode 100644 index 0000000..9268f2d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-16-15-08-12.bpo-37205.Wh5svI.rst @@ -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. diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 80eab30..4caacc3 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -51,7 +51,7 @@ #define _Py_tzname tzname #endif -#if defined(__APPLE__ ) && defined(__has_builtin) +#if defined(__APPLE__ ) && defined(__has_builtin) # if __has_builtin(__builtin_available) # define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) # endif @@ -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 * 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); } @@ -91,7 +102,10 @@ Fractions of a second may be present if the system clock provides them."); static PyObject * 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); } @@ -147,20 +161,11 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) } #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 __APPLE__ -/* +/* * The clock_* functions will be removed from the module * dict entirely when the C API is not available. */ @@ -1096,10 +1101,22 @@ the local timezone used by methods such as localtime, but this behaviour\n\ should not be relied on."); #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 * 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); } @@ -1111,7 +1128,10 @@ Monotonic clock, cannot go backward."); static PyObject * 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); } @@ -1120,10 +1140,23 @@ PyDoc_STRVAR(monotonic_ns_doc, \n\ 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 * 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, @@ -1131,10 +1164,14 @@ PyDoc_STRVAR(perf_counter_doc, \n\ Performance counter for benchmarking."); + static PyObject * 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); } @@ -1421,7 +1458,7 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) #if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability) static int -_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) __attribute__((availability(macos, introduced=10.12))) __attribute__((availability(ios, introduced=10.0))) __attribute__((availability(tvos, introduced=10.0))) @@ -1460,7 +1497,7 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) #ifdef HAVE_THREAD_TIME #ifdef __APPLE__ -/* +/* * The clock_* functions will be removed from the module * dict entirely when the C API is not available. */ @@ -2025,7 +2062,10 @@ pysleep(_PyTime_t secs) HANDLE hInterruptEvent; #endif - deadline = _PyTime_GetMonotonicClock() + secs; + if (get_monotonic(&monotonic) < 0) { + return -1; + } + deadline = monotonic + secs; do { #ifndef MS_WINDOWS @@ -2077,10 +2117,13 @@ pysleep(_PyTime_t secs) if (PyErr_CheckSignals()) return -1; - monotonic = _PyTime_GetMonotonicClock(); + if (get_monotonic(&monotonic) < 0) { + return -1; + } secs = deadline - monotonic; - if (secs < 0) + if (secs < 0) { break; + } /* retry with the recomputed delay */ } while (1); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2d43e01..33deafb 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -763,12 +763,6 @@ pycore_interp_init(PyThreadState *tstate) goto done; } - if (_Py_IsMainInterpreter(tstate)) { - if (_PyTime_Init() < 0) { - return _PyStatus_ERR("can't initialize time"); - } - } - status = _PySys_Create(tstate, &sysmod); if (_PyStatus_EXCEPTION(status)) { goto done; diff --git a/Python/pytime.c b/Python/pytime.c index 179bced..1ef99ae 100644 --- a/Python/pytime.c +++ b/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 (raise) { _PyTime_overflow(); + res = -1; } - res = -1; t = (t > 0) ? _PyTime_MAX : _PyTime_MIN; } else { @@ -318,8 +318,8 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise) if (t > _PyTime_MAX - nsec) { if (raise) { _PyTime_overflow(); + res = -1; } - res = -1; t = _PyTime_MAX; } 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 (raise) { _PyTime_overflow(); + res = -1; } - res = -1; t = (t > 0) ? _PyTime_MAX : _PyTime_MIN; } else { @@ -364,8 +364,8 @@ pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise) if (t > _PyTime_MAX - usec) { if (raise) { _PyTime_overflow(); + res = -1; } - res = -1; t = _PyTime_MAX; } else { @@ -656,7 +656,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) #endif 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 FILETIME system_time; @@ -769,9 +769,10 @@ _PyTime_t _PyTime_GetSystemClock(void) { _PyTime_t t; - if (pygettimeofday(&t, NULL, 0) < 0) { - /* should not happen, _PyTime_Init() checked the clock at startup */ - Py_FatalError("pygettimeofday() failed"); + if (py_get_system_clock(&t, NULL, 0) < 0) { + // If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails: + // silently ignore the failure and return 0. + t = 0; } return t; } @@ -779,11 +780,61 @@ _PyTime_GetSystemClock(void) int _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 +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 -pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) +py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { #if defined(MS_WINDOWS) ULONGLONG ticks; @@ -800,10 +851,12 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) _PyTime_overflow(); return -1; } - /* Hello, time traveler! */ - Py_FatalError("pymonotonic: integer overflow"); + // Truncate to _PyTime_MAX silently. + *tp = _PyTime_MAX; + } + else { + *tp = t * MS_TO_NS; } - *tp = t * MS_TO_NS; if (info) { DWORD timeAdjustment, timeIncrement; @@ -821,56 +874,23 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) } #elif defined(__APPLE__) - static mach_timebase_info_data_t timebase; - uint64_t ticks; - - 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); - - /* 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"); + 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) { return -1; } } if (info) { 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->adjustable = 0; } - ticks = mach_absolute_time(); - *tp = _PyTime_MulDiv(ticks, - (_PyTime_t)timebase.numer, - (_PyTime_t)timebase.denom); + uint64_t ticks = mach_absolute_time(); + *tp = _PyTime_MulDiv((_PyTime_t)ticks, timebase_numer, timebase_denom); #elif defined(__hpux) hrtime_t time; @@ -934,10 +954,10 @@ _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 */ - Py_FatalError("pymonotonic() failed"); + if (py_get_monotonic_clock(&t, NULL, 0) < 0) { + // If mach_timebase_info(), clock_gettime() or gethrtime() fails: + // silently ignore the failure and return 0. + t = 0; } return t; } @@ -945,54 +965,69 @@ _PyTime_GetMonotonicClock(void) int _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 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; - LARGE_INTEGER now; - LONGLONG ticksll; - _PyTime_t ticks; + LONGLONG frequency; - if (frequency == 0) { - LARGE_INTEGER freq; - if (!QueryPerformanceFrequency(&freq)) { + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) { + if (raise) { PyErr_SetFromWindowsErr(0); - return -1; } - frequency = freq.QuadPart; + return -1; + } + frequency = freq.QuadPart; - /* Sanity check: should never occur in practice */ - if (frequency < 1) { + /* Sanity check: should never occur in practice */ + if (frequency < 1) { + if (raise) { PyErr_SetString(PyExc_RuntimeError, "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 - _PyTime_MulDiv(), with ticks < frequency. + Make also sure that (ticks * SEC_TO_NS) cannot overflow in + _PyTime_MulDiv(), with ticks < frequency. - Known QueryPerformanceFrequency() values: + Known QueryPerformanceFrequency() values: - * 10,000,000 (10 MHz): 100 ns resolution - * 3,579,545 Hz (3.6 MHz): 279 ns resolution + * 10,000,000 (10 MHz): 100 ns resolution + * 3,579,545 Hz (3.6 MHz): 279 ns resolution - None of these frequencies can overflow with 64-bit _PyTime_t, but - check for overflow, just in case. */ - if (frequency > _PyTime_MAX - || frequency > (LONGLONG)_PyTime_MAX / (LONGLONG)SEC_TO_NS) { + None of these frequencies can overflow with 64-bit _PyTime_t, but + check for overflow, just in case. */ + if (frequency > _PyTime_MAX + || frequency > (LONGLONG)_PyTime_MAX / (LONGLONG)SEC_TO_NS) + { + if (raise) { PyErr_SetString(PyExc_OverflowError, "QueryPerformanceFrequency is too large"); - return -1; } + return -1; + } - QueryPerformanceCounter(&now); + *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; + } } if (info) { @@ -1002,11 +1037,13 @@ win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info) info->adjustable = 0; } + LARGE_INTEGER now; QueryPerformanceCounter(&now); - ticksll = now.QuadPart; + LONGLONG ticksll = now.QuadPart; /* Make sure that casting LONGLONG to _PyTime_t cannot overflow, both types are signed */ + _PyTime_t ticks; Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks)); ticks = (_PyTime_t)ticksll; @@ -1020,7 +1057,7 @@ int _PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info) { #ifdef MS_WINDOWS - return win_perf_counter(t, info); + return py_get_win_perf_counter(t, info, 1); #else return _PyTime_GetMonotonicClockWithInfo(t, info); #endif @@ -1031,33 +1068,22 @@ _PyTime_t _PyTime_GetPerfCounter(void) { _PyTime_t t; - if (_PyTime_GetPerfCounterWithInfo(&t, NULL)) { - Py_FatalError("_PyTime_GetPerfCounterWithInfo() failed"); + int res; +#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; } 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 _PyTime_localtime(time_t t, struct tm *tm) { #ifdef MS_WINDOWS -- cgit v0.12