mirror of https://github.com/python/cpython
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
|
@ -0,0 +1,83 @@
|
|||
.. highlight:: c
|
||||
|
||||
PyTime C API
|
||||
============
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
The clock C API provides access to system clocks.
|
||||
It is similar to the Python :mod:`time` module.
|
||||
|
||||
For C API related to the :mod:`datetime` module, see :ref:`datetimeobjects`.
|
||||
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
.. c:type:: PyTime_t
|
||||
|
||||
A timestamp or duration in nanoseconds, represented as a signed 64-bit
|
||||
integer.
|
||||
|
||||
The reference point for timestamps depends on the clock used. For example,
|
||||
:c:func:`PyTime_Time` returns timestamps relative to the UNIX epoch.
|
||||
|
||||
The supported range is around [-292.3 years; +292.3 years].
|
||||
Using the Unix epoch (January 1st, 1970) as reference, the supported date
|
||||
range is around [1677-09-21; 2262-04-11].
|
||||
The exact limits are exposed as constants:
|
||||
|
||||
.. c:var:: PyTime_t PyTime_MIN
|
||||
|
||||
Minimum value of :c:type:`PyTime_t`.
|
||||
|
||||
.. c:var:: PyTime_t PyTime_MAX
|
||||
|
||||
Maximum value of :c:type:`PyTime_t`.
|
||||
|
||||
|
||||
Clock Functions
|
||||
---------------
|
||||
|
||||
The following functions take a pointer to a :c:expr:`PyTime_t` that they
|
||||
set to the value of a particular clock.
|
||||
Details of each clock are given in the documentation of the corresponding
|
||||
Python function.
|
||||
|
||||
The functions return ``0`` on success, or ``-1`` (with an exception set)
|
||||
on failure.
|
||||
|
||||
On integer overflow, they set the :c:data:`PyExc_OverflowError` exception and
|
||||
set ``*result`` to the value clamped to the ``[PyTime_MIN; PyTime_MAX]``
|
||||
range.
|
||||
(On current systems, integer overflows are likely caused by misconfigured
|
||||
system time.)
|
||||
|
||||
As any other C API (unless otherwise specified), the functions must be called
|
||||
with the :term:`GIL` held.
|
||||
|
||||
.. c:function:: int PyTime_Monotonic(PyTime_t *result)
|
||||
|
||||
Read the monotonic clock.
|
||||
See :func:`time.monotonic` for important details on this clock.
|
||||
|
||||
.. c:function:: int PyTime_PerfCounter(PyTime_t *result)
|
||||
|
||||
Read the performance counter.
|
||||
See :func:`time.perf_counter` for important details on this clock.
|
||||
|
||||
.. c:function:: int PyTime_Time(PyTime_t *result)
|
||||
|
||||
Read the “wall clock” time.
|
||||
See :func:`time.time` for details important on this clock.
|
||||
|
||||
|
||||
Conversion functions
|
||||
--------------------
|
||||
|
||||
.. c:function:: double PyTime_AsSecondsDouble(PyTime_t t)
|
||||
|
||||
Convert a timestamp to a number of seconds as a C :c:expr:`double`.
|
||||
|
||||
The function cannot fail, but note that :c:expr:`double` has limited
|
||||
accuracy for large values.
|
|
@ -20,4 +20,5 @@ and parsing function arguments and constructing Python values from C values.
|
|||
hash.rst
|
||||
reflection.rst
|
||||
codec.rst
|
||||
time.rst
|
||||
perfmaps.rst
|
||||
|
|
13
Doc/conf.py
13
Doc/conf.py
|
@ -135,11 +135,14 @@ nitpick_ignore = [
|
|||
('c:type', 'wchar_t'),
|
||||
('c:type', '__int64'),
|
||||
('c:type', 'unsigned __int64'),
|
||||
('c:type', 'double'),
|
||||
# Standard C structures
|
||||
('c:struct', 'in6_addr'),
|
||||
('c:struct', 'in_addr'),
|
||||
('c:struct', 'stat'),
|
||||
('c:struct', 'statvfs'),
|
||||
('c:struct', 'timeval'),
|
||||
('c:struct', 'timespec'),
|
||||
# Standard C macros
|
||||
('c:macro', 'LLONG_MAX'),
|
||||
('c:macro', 'LLONG_MIN'),
|
||||
|
@ -269,12 +272,12 @@ nitpick_ignore += [
|
|||
('py:meth', 'index'), # list.index, tuple.index, etc.
|
||||
]
|
||||
|
||||
# gh-106948: Copy standard C types declared in the "c:type" domain to the
|
||||
# "c:identifier" domain, since "c:function" markup looks for types in the
|
||||
# "c:identifier" domain. Use list() to not iterate on items which are being
|
||||
# added
|
||||
# gh-106948: Copy standard C types declared in the "c:type" domain and C
|
||||
# structures declared in the "c:struct" domain to the "c:identifier" domain,
|
||||
# since "c:function" markup looks for types in the "c:identifier" domain. Use
|
||||
# list() to not iterate on items which are being added
|
||||
for role, name in list(nitpick_ignore):
|
||||
if role == 'c:type':
|
||||
if role in ('c:type', 'c:struct'):
|
||||
nitpick_ignore.append(('c:identifier', name))
|
||||
del role, name
|
||||
|
||||
|
|
|
@ -1516,6 +1516,16 @@ New Features
|
|||
* Add :c:func:`Py_HashPointer` function to hash a pointer.
|
||||
(Contributed by Victor Stinner in :gh:`111545`.)
|
||||
|
||||
* Add PyTime C API:
|
||||
|
||||
* :c:type:`PyTime_t` type.
|
||||
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
|
||||
* :c:func:`PyTime_AsSecondsDouble`
|
||||
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
|
||||
:c:func:`PyTime_Time` functions.
|
||||
|
||||
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
|
||||
|
||||
|
||||
Porting to Python 3.13
|
||||
----------------------
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
#include "weakrefobject.h"
|
||||
#include "structseq.h"
|
||||
#include "cpython/picklebufobject.h"
|
||||
#include "cpython/pytime.h"
|
||||
#include "codecs.h"
|
||||
#include "pyerrors.h"
|
||||
#include "pythread.h"
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// PyTime_t C API: see Doc/c-api/time.rst for the documentation.
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
#ifndef Py_PYTIME_H
|
||||
#define Py_PYTIME_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int64_t PyTime_t;
|
||||
#define PyTime_MIN INT64_MIN
|
||||
#define PyTime_MAX INT64_MAX
|
||||
|
||||
PyAPI_FUNC(double) PyTime_AsSecondsDouble(PyTime_t t);
|
||||
PyAPI_FUNC(int) PyTime_Monotonic(PyTime_t *result);
|
||||
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
|
||||
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* Py_PYTIME_H */
|
||||
#endif /* Py_LIMITED_API */
|
|
@ -1,34 +1,39 @@
|
|||
// The _PyTime_t API is written to use timestamp and timeout values stored in
|
||||
// various formats and to read clocks.
|
||||
// Internal PyTime_t C API: see Doc/c-api/time.rst for the documentation.
|
||||
//
|
||||
// The _PyTime_t type is an integer to support directly common arithmetic
|
||||
// operations like t1 + t2.
|
||||
// The PyTime_t type is an integer to support directly common arithmetic
|
||||
// operations such as t1 + t2.
|
||||
//
|
||||
// The _PyTime_t API supports a resolution of 1 nanosecond. The _PyTime_t type
|
||||
// is signed to support negative timestamps. The supported range is around
|
||||
// [-292.3 years; +292.3 years]. Using the Unix epoch (January 1st, 1970), the
|
||||
// supported date range is around [1677-09-21; 2262-04-11].
|
||||
// Time formats:
|
||||
//
|
||||
// Formats:
|
||||
// * Seconds.
|
||||
// * Seconds as a floating point number (C double).
|
||||
// * Milliseconds (10^-3 seconds).
|
||||
// * Microseconds (10^-6 seconds).
|
||||
// * 100 nanoseconds (10^-7 seconds), used on Windows.
|
||||
// * Nanoseconds (10^-9 seconds).
|
||||
// * timeval structure, 1 microsecond (10^-6 seconds).
|
||||
// * timespec structure, 1 nanosecond (10^-9 seconds).
|
||||
//
|
||||
// * seconds
|
||||
// * seconds as a floating pointer number (C double)
|
||||
// * milliseconds (10^-3 seconds)
|
||||
// * microseconds (10^-6 seconds)
|
||||
// * 100 nanoseconds (10^-7 seconds)
|
||||
// * nanoseconds (10^-9 seconds)
|
||||
// * timeval structure, 1 microsecond resolution (10^-6 seconds)
|
||||
// * timespec structure, 1 nanosecond resolution (10^-9 seconds)
|
||||
// Note that PyTime_t is now specified as int64_t, in nanoseconds.
|
||||
// (If we need to change this, we'll need new public API with new names.)
|
||||
// Previously, PyTime_t was configurable (in theory); some comments and code
|
||||
// might still allude to that.
|
||||
//
|
||||
// Integer overflows are detected and raise OverflowError. Conversion to a
|
||||
// resolution worse than 1 nanosecond is rounded correctly with the requested
|
||||
// rounding mode. There are 4 rounding modes: floor (towards -inf), ceiling
|
||||
// (towards +inf), half even and up (away from zero).
|
||||
// resolution larger than 1 nanosecond is rounded correctly with the requested
|
||||
// rounding mode. Available rounding modes:
|
||||
//
|
||||
// Some functions clamp the result in the range [_PyTime_MIN; _PyTime_MAX], so
|
||||
// the caller doesn't have to handle errors and doesn't need to hold the GIL.
|
||||
// For example, _PyTime_Add(t1, t2) computes t1+t2 and clamp the result on
|
||||
// overflow.
|
||||
// * Round towards minus infinity (-inf). For example, used to read a clock.
|
||||
// * Round towards infinity (+inf). For example, used for timeout to wait "at
|
||||
// least" N seconds.
|
||||
// * Round to nearest with ties going to nearest even integer. For example, used
|
||||
// to round from a Python float.
|
||||
// * Round away from zero. For example, used for timeout.
|
||||
//
|
||||
// Some functions clamp the result in the range [PyTime_MIN; PyTime_MAX]. The
|
||||
// caller doesn't have to handle errors and so doesn't need to hold the GIL to
|
||||
// handle exceptions. For example, _PyTime_Add(t1, t2) computes t1+t2 and
|
||||
// clamps the result on overflow.
|
||||
//
|
||||
// Clocks:
|
||||
//
|
||||
|
@ -36,10 +41,11 @@
|
|||
// * Monotonic clock
|
||||
// * Performance counter
|
||||
//
|
||||
// Operations like (t * k / q) with integers are implemented in a way to reduce
|
||||
// the risk of integer overflow. Such operation is used to convert a clock
|
||||
// value expressed in ticks with a frequency to _PyTime_t, like
|
||||
// QueryPerformanceCounter() with QueryPerformanceFrequency().
|
||||
// Internally, operations like (t * k / q) with integers are implemented in a
|
||||
// way to reduce the risk of integer overflow. Such operation is used to convert a
|
||||
// clock value expressed in ticks with a frequency to PyTime_t, like
|
||||
// QueryPerformanceCounter() with QueryPerformanceFrequency() on Windows.
|
||||
|
||||
|
||||
#ifndef Py_INTERNAL_TIME_H
|
||||
#define Py_INTERNAL_TIME_H
|
||||
|
@ -56,14 +62,7 @@ extern "C" {
|
|||
struct timeval;
|
||||
#endif
|
||||
|
||||
// _PyTime_t: Python timestamp with subsecond precision. It can be used to
|
||||
// store a duration, and so indirectly a date (related to another date, like
|
||||
// UNIX epoch).
|
||||
typedef int64_t _PyTime_t;
|
||||
// _PyTime_MIN nanoseconds is around -292.3 years
|
||||
#define _PyTime_MIN INT64_MIN
|
||||
// _PyTime_MAX nanoseconds is around +292.3 years
|
||||
#define _PyTime_MAX INT64_MAX
|
||||
typedef PyTime_t _PyTime_t;
|
||||
#define _SIZEOF_PYTIME_T 8
|
||||
|
||||
typedef enum {
|
||||
|
@ -147,7 +146,7 @@ PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t
|
|||
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
|
||||
|
||||
// Create a timestamp from a number of microseconds.
|
||||
// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
|
||||
// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
|
||||
extern _PyTime_t _PyTime_FromMicrosecondsClamp(_PyTime_t us);
|
||||
|
||||
// Create a timestamp from nanoseconds (Python int).
|
||||
|
@ -169,10 +168,6 @@ PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
|
|||
PyObject *obj,
|
||||
_PyTime_round_t round);
|
||||
|
||||
// Convert a timestamp to a number of seconds as a C double.
|
||||
// Export for '_socket' shared extension.
|
||||
PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
|
||||
|
||||
// Convert timestamp to a number of milliseconds (10^-3 seconds).
|
||||
// Export for '_ssl' shared extension.
|
||||
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
||||
|
@ -183,9 +178,6 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
|||
PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
|
||||
_PyTime_round_t round);
|
||||
|
||||
// Convert timestamp to a number of nanoseconds (10^-9 seconds).
|
||||
extern _PyTime_t _PyTime_AsNanoseconds(_PyTime_t t);
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
// Convert timestamp to a number of 100 nanoseconds (10^-7 seconds).
|
||||
extern _PyTime_t _PyTime_As100Nanoseconds(_PyTime_t t,
|
||||
|
@ -250,7 +242,7 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
|
|||
#endif
|
||||
|
||||
|
||||
// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
|
||||
// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
|
||||
extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2);
|
||||
|
||||
// Structure used by time.get_clock_info()
|
||||
|
@ -267,7 +259,8 @@ typedef struct {
|
|||
// On integer overflow, silently ignore the overflow and clamp the clock to
|
||||
// [_PyTime_MIN; _PyTime_MAX].
|
||||
//
|
||||
// Use _PyTime_GetSystemClockWithInfo() to check for failure.
|
||||
// Use _PyTime_GetSystemClockWithInfo or the public PyTime_Time() to check
|
||||
// for failure.
|
||||
// Export for '_random' shared extension.
|
||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
|
||||
|
||||
|
@ -287,7 +280,8 @@ extern int _PyTime_GetSystemClockWithInfo(
|
|||
// On integer overflow, silently ignore the overflow and clamp the clock to
|
||||
// [_PyTime_MIN; _PyTime_MAX].
|
||||
//
|
||||
// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
|
||||
// Use _PyTime_GetMonotonicClockWithInfo or the public PyTime_Monotonic()
|
||||
// to check for failure.
|
||||
// Export for '_random' shared extension.
|
||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
|
||||
|
||||
|
@ -322,10 +316,12 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
|
|||
// On integer overflow, silently ignore the overflow and clamp the clock to
|
||||
// [_PyTime_MIN; _PyTime_MAX].
|
||||
//
|
||||
// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
|
||||
// Use _PyTime_GetPerfCounterWithInfo() or the public PyTime_PerfCounter
|
||||
// to check for failure.
|
||||
// Export for '_lsprof' shared extension.
|
||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
|
||||
|
||||
|
||||
// Get the performance counter: clock with the highest available resolution to
|
||||
// measure a short duration.
|
||||
//
|
||||
|
@ -336,6 +332,13 @@ extern int _PyTime_GetPerfCounterWithInfo(
|
|||
_PyTime_t *t,
|
||||
_Py_clock_info_t *info);
|
||||
|
||||
// Alias for backward compatibility
|
||||
#define _PyTime_MIN PyTime_MIN
|
||||
#define _PyTime_MAX PyTime_MAX
|
||||
#define _PyTime_AsSecondsDouble PyTime_AsSecondsDouble
|
||||
|
||||
|
||||
// --- _PyDeadline -----------------------------------------------------------
|
||||
|
||||
// Create a deadline.
|
||||
// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import time
|
||||
import unittest
|
||||
from test.support import import_helper
|
||||
_testcapi = import_helper.import_module('_testcapi')
|
||||
|
||||
|
||||
PyTime_MIN = _testcapi.PyTime_MIN
|
||||
PyTime_MAX = _testcapi.PyTime_MAX
|
||||
SEC_TO_NS = 10 ** 9
|
||||
DAY_TO_SEC = (24 * 60 * 60)
|
||||
# Worst clock resolution: maximum delta between two clock reads.
|
||||
CLOCK_RES = 0.050
|
||||
|
||||
|
||||
class CAPITest(unittest.TestCase):
|
||||
def test_min_max(self):
|
||||
# PyTime_t is just int64_t
|
||||
self.assertEqual(PyTime_MIN, -2**63)
|
||||
self.assertEqual(PyTime_MAX, 2**63 - 1)
|
||||
|
||||
def check_clock(self, c_func, py_func):
|
||||
t1 = c_func()
|
||||
t2 = py_func()
|
||||
self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
|
||||
|
||||
def test_assecondsdouble(self):
|
||||
# Test PyTime_AsSecondsDouble()
|
||||
def ns_to_sec(ns):
|
||||
if abs(ns) % SEC_TO_NS == 0:
|
||||
return float(ns // SEC_TO_NS)
|
||||
else:
|
||||
return float(ns) / SEC_TO_NS
|
||||
|
||||
seconds = (
|
||||
0,
|
||||
1,
|
||||
DAY_TO_SEC,
|
||||
365 * DAY_TO_SEC,
|
||||
)
|
||||
values = {
|
||||
PyTime_MIN,
|
||||
PyTime_MIN + 1,
|
||||
PyTime_MAX - 1,
|
||||
PyTime_MAX,
|
||||
}
|
||||
for second in seconds:
|
||||
ns = second * SEC_TO_NS
|
||||
values.add(ns)
|
||||
# test nanosecond before/after to test rounding
|
||||
values.add(ns - 1)
|
||||
values.add(ns + 1)
|
||||
for ns in list(values):
|
||||
if (-ns) > PyTime_MAX:
|
||||
continue
|
||||
values.add(-ns)
|
||||
for ns in sorted(values):
|
||||
with self.subTest(ns=ns):
|
||||
self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
|
||||
ns_to_sec(ns))
|
||||
|
||||
def test_monotonic(self):
|
||||
# Test PyTime_Monotonic()
|
||||
self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
|
||||
|
||||
def test_perf_counter(self):
|
||||
# Test PyTime_PerfCounter()
|
||||
self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
|
||||
|
||||
def test_time(self):
|
||||
# Test PyTime_time()
|
||||
self.check_clock(_testcapi.PyTime_Time, time.time)
|
|
@ -43,8 +43,8 @@ class _PyTime(enum.IntEnum):
|
|||
ROUND_UP = 3
|
||||
|
||||
# _PyTime_t is int64_t
|
||||
_PyTime_MIN = -2 ** 63
|
||||
_PyTime_MAX = 2 ** 63 - 1
|
||||
PyTime_MIN = -2 ** 63
|
||||
PyTime_MAX = 2 ** 63 - 1
|
||||
|
||||
# Rounding modes supported by PyTime
|
||||
ROUNDING_MODES = (
|
||||
|
@ -934,7 +934,7 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
|
|||
_PyTime_FromSecondsObject(float('nan'), time_rnd)
|
||||
|
||||
def test_AsSecondsDouble(self):
|
||||
from _testinternalcapi import _PyTime_AsSecondsDouble
|
||||
from _testcapi import PyTime_AsSecondsDouble
|
||||
|
||||
def float_converter(ns):
|
||||
if abs(ns) % SEC_TO_NS == 0:
|
||||
|
@ -942,15 +942,10 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
|
|||
else:
|
||||
return float(ns) / SEC_TO_NS
|
||||
|
||||
self.check_int_rounding(lambda ns, rnd: _PyTime_AsSecondsDouble(ns),
|
||||
self.check_int_rounding(lambda ns, rnd: PyTime_AsSecondsDouble(ns),
|
||||
float_converter,
|
||||
NS_TO_SEC)
|
||||
|
||||
# test nan
|
||||
for time_rnd, _ in ROUNDING_MODES:
|
||||
with self.assertRaises(TypeError):
|
||||
_PyTime_AsSecondsDouble(float('nan'))
|
||||
|
||||
def create_decimal_converter(self, denominator):
|
||||
denom = decimal.Decimal(denominator)
|
||||
|
||||
|
@ -1009,7 +1004,7 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
|
|||
tv_sec_max = self.time_t_max
|
||||
tv_sec_min = self.time_t_min
|
||||
|
||||
for t in (_PyTime_MIN, _PyTime_MAX):
|
||||
for t in (PyTime_MIN, PyTime_MAX):
|
||||
ts = _PyTime_AsTimeval_clamp(t, _PyTime.ROUND_CEILING)
|
||||
with decimal.localcontext() as context:
|
||||
context.rounding = decimal.ROUND_CEILING
|
||||
|
@ -1028,7 +1023,7 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
|
|||
def test_AsTimespec_clamp(self):
|
||||
from _testinternalcapi import _PyTime_AsTimespec_clamp
|
||||
|
||||
for t in (_PyTime_MIN, _PyTime_MAX):
|
||||
for t in (PyTime_MIN, PyTime_MAX):
|
||||
ts = _PyTime_AsTimespec_clamp(t)
|
||||
tv_sec, tv_nsec = divmod(t, NS_TO_SEC)
|
||||
if self.time_t_max < tv_sec:
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Add PyTime C API:
|
||||
|
||||
* :c:type:`PyTime_t` type.
|
||||
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
|
||||
* :c:func:`PyTime_AsSecondsDouble`,
|
||||
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
|
||||
:c:func:`PyTime_Time` functions.
|
||||
|
||||
Patch by Victor Stinner.
|
|
@ -162,7 +162,7 @@
|
|||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c _testcapi/time.c
|
||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ random_seed_urandom(RandomObject *self)
|
|||
static void
|
||||
random_seed_time_pid(RandomObject *self)
|
||||
{
|
||||
_PyTime_t now;
|
||||
PyTime_t now;
|
||||
uint32_t key[5];
|
||||
|
||||
now = _PyTime_GetSystemClock();
|
||||
|
|
|
@ -59,6 +59,7 @@ int _PyTestCapi_Init_Immortal(PyObject *module);
|
|||
int _PyTestCapi_Init_GC(PyObject *module);
|
||||
int _PyTestCapi_Init_Sys(PyObject *module);
|
||||
int _PyTestCapi_Init_Hash(PyObject *module);
|
||||
int _PyTestCapi_Init_Time(PyObject *module);
|
||||
|
||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
||||
int _PyTestCapi_Init_HeaptypeRelative(PyObject *module);
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
#include "parts.h"
|
||||
|
||||
|
||||
static int
|
||||
pytime_from_nanoseconds(PyTime_t *tp, PyObject *obj)
|
||||
{
|
||||
if (!PyLong_Check(obj)) {
|
||||
PyErr_Format(PyExc_TypeError, "expect int, got %s",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
long long nsec = PyLong_AsLongLong(obj);
|
||||
if (nsec == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t));
|
||||
*tp = (PyTime_t)nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
test_pytime_assecondsdouble(PyObject *Py_UNUSED(self), PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
||||
return NULL;
|
||||
}
|
||||
PyTime_t ts;
|
||||
if (pytime_from_nanoseconds(&ts, obj) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
double d = PyTime_AsSecondsDouble(ts);
|
||||
return PyFloat_FromDouble(d);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
pytime_as_float(PyTime_t t)
|
||||
{
|
||||
return PyFloat_FromDouble(PyTime_AsSecondsDouble(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PyObject*
|
||||
test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||
{
|
||||
PyTime_t t;
|
||||
if (PyTime_Monotonic(&t) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return pytime_as_float(t);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||
{
|
||||
PyTime_t t;
|
||||
if (PyTime_PerfCounter(&t) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return pytime_as_float(t);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||
{
|
||||
PyTime_t t;
|
||||
if (PyTime_Time(&t) < 0) {
|
||||
printf("ERR! %d\n", (int)t);
|
||||
return NULL;
|
||||
}
|
||||
printf("... %d\n", (int)t);
|
||||
return pytime_as_float(t);
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef test_methods[] = {
|
||||
{"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
|
||||
{"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS},
|
||||
{"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS},
|
||||
{"PyTime_Time", test_pytime_time, METH_NOARGS},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
int
|
||||
_PyTestCapi_Init_Time(PyObject *m)
|
||||
{
|
||||
if (PyModule_AddFunctions(m, test_methods) < 0) {
|
||||
return -1;
|
||||
}
|
||||
Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t));
|
||||
if (PyModule_AddObject(m, "PyTime_MIN", PyLong_FromLongLong(PyTime_MIN)) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (PyModule_AddObject(m, "PyTime_MAX", PyLong_FromLongLong(PyTime_MAX)) < 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -4107,6 +4107,9 @@ PyInit__testcapi(void)
|
|||
if (_PyTestCapi_Init_Hash(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_Time(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyState_AddModule(m, &_testcapimodule);
|
||||
return m;
|
||||
|
|
|
@ -52,21 +52,6 @@ test_pytime_fromsecondsobject(PyObject *self, PyObject *args)
|
|||
return _PyTime_AsNanosecondsObject(ts);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
test_pytime_assecondsdouble(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *obj;
|
||||
if (!PyArg_ParseTuple(args, "O", &obj)) {
|
||||
return NULL;
|
||||
}
|
||||
_PyTime_t ts;
|
||||
if (_PyTime_FromNanosecondsObject(&ts, obj) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
double d = _PyTime_AsSecondsDouble(ts);
|
||||
return PyFloat_FromDouble(d);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
test_PyTime_AsTimeval(PyObject *self, PyObject *args)
|
||||
{
|
||||
|
@ -254,7 +239,6 @@ test_pytime_object_to_timespec(PyObject *self, PyObject *args)
|
|||
static PyMethodDef TestMethods[] = {
|
||||
{"_PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
|
||||
{"_PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
|
||||
{"_PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
{"_PyTime_AsTimespec", test_PyTime_AsTimespec, METH_VARARGS},
|
||||
{"_PyTime_AsTimespec_clamp", test_PyTime_AsTimespec_clamp, METH_VARARGS},
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\codec.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\sys.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\hash.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\time.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -105,6 +105,9 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\hash.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\time.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
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…
Reference in New Issue