mirror of https://github.com/python/cpython
gh-110850: Add PyTime_TimeRaw() function (#118394)
Add "Raw" variant of PyTime functions: * PyTime_MonotonicRaw() * PyTime_PerfCounterRaw() * PyTime_TimeRaw() Changes: * Add documentation and tests. Tests release the GIL while calling raw clock functions. * py_get_system_clock() and py_get_monotonic_clock() now check that the GIL is hold by the caller if raise_exc is non-zero. * Reimplement "Unchecked" functions with raw clock functions. Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
a8bcf3ec32
commit
b52c753e0f
|
@ -72,6 +72,35 @@ with the :term:`GIL` held.
|
||||||
See :func:`time.time` for details important on this clock.
|
See :func:`time.time` for details important on this clock.
|
||||||
|
|
||||||
|
|
||||||
|
Raw Clock Functions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Similar to clock functions, but don't set an exception on error and don't
|
||||||
|
require the caller to hold the GIL.
|
||||||
|
|
||||||
|
On success, the functions return ``0``.
|
||||||
|
|
||||||
|
On failure, they set ``*result`` to ``0`` and return ``-1``, *without* setting
|
||||||
|
an exception. To get the cause of the error, acquire the GIL and call the
|
||||||
|
regular (non-``Raw``) function. Note that the regular function may succeed after
|
||||||
|
the ``Raw`` one failed.
|
||||||
|
|
||||||
|
.. c:function:: int PyTime_MonotonicRaw(PyTime_t *result)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyTime_Monotonic`,
|
||||||
|
but don't set an exception on error and don't require holding the GIL.
|
||||||
|
|
||||||
|
.. c:function:: int PyTime_PerfCounterRaw(PyTime_t *result)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyTime_PerfCounter`,
|
||||||
|
but don't set an exception on error and don't require holding the GIL.
|
||||||
|
|
||||||
|
.. c:function:: int PyTime_TimeRaw(PyTime_t *result)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyTime_Time`,
|
||||||
|
but don't set an exception on error and don't require holding the GIL.
|
||||||
|
|
||||||
|
|
||||||
Conversion functions
|
Conversion functions
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|
|
@ -1901,9 +1901,15 @@ New Features
|
||||||
|
|
||||||
* :c:type:`PyTime_t` type.
|
* :c:type:`PyTime_t` type.
|
||||||
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
|
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
|
||||||
* :c:func:`PyTime_AsSecondsDouble`
|
* Add functions:
|
||||||
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
|
|
||||||
:c:func:`PyTime_Time` functions.
|
* :c:func:`PyTime_AsSecondsDouble`.
|
||||||
|
* :c:func:`PyTime_Monotonic`.
|
||||||
|
* :c:func:`PyTime_MonotonicRaw`.
|
||||||
|
* :c:func:`PyTime_PerfCounter`.
|
||||||
|
* :c:func:`PyTime_PerfCounterRaw`.
|
||||||
|
* :c:func:`PyTime_Time`.
|
||||||
|
* :c:func:`PyTime_TimeRaw`.
|
||||||
|
|
||||||
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
|
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ PyAPI_FUNC(int) PyTime_Monotonic(PyTime_t *result);
|
||||||
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
|
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
|
||||||
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);
|
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PyTime_MonotonicRaw(PyTime_t *result);
|
||||||
|
PyAPI_FUNC(int) PyTime_PerfCounterRaw(PyTime_t *result);
|
||||||
|
PyAPI_FUNC(int) PyTime_TimeRaw(PyTime_t *result);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,11 +18,6 @@ class CAPITest(unittest.TestCase):
|
||||||
self.assertEqual(PyTime_MIN, -2**63)
|
self.assertEqual(PyTime_MIN, -2**63)
|
||||||
self.assertEqual(PyTime_MAX, 2**63 - 1)
|
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):
|
def test_assecondsdouble(self):
|
||||||
# Test PyTime_AsSecondsDouble()
|
# Test PyTime_AsSecondsDouble()
|
||||||
def ns_to_sec(ns):
|
def ns_to_sec(ns):
|
||||||
|
@ -58,14 +53,22 @@ class CAPITest(unittest.TestCase):
|
||||||
self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
|
self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
|
||||||
ns_to_sec(ns))
|
ns_to_sec(ns))
|
||||||
|
|
||||||
|
def check_clock(self, c_func, py_func):
|
||||||
|
t1 = c_func()
|
||||||
|
t2 = py_func()
|
||||||
|
self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
|
||||||
|
|
||||||
def test_monotonic(self):
|
def test_monotonic(self):
|
||||||
# Test PyTime_Monotonic()
|
# Test PyTime_Monotonic() and PyTime_MonotonicRaw()
|
||||||
self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
|
self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
|
||||||
|
self.check_clock(_testcapi.PyTime_MonotonicRaw, time.monotonic)
|
||||||
|
|
||||||
def test_perf_counter(self):
|
def test_perf_counter(self):
|
||||||
# Test PyTime_PerfCounter()
|
# Test PyTime_PerfCounter() and PyTime_PerfCounterRaw()
|
||||||
self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
|
self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
|
||||||
|
self.check_clock(_testcapi.PyTime_PerfCounterRaw, time.perf_counter)
|
||||||
|
|
||||||
def test_time(self):
|
def test_time(self):
|
||||||
# Test PyTime_time()
|
# Test PyTime_Time() and PyTime_TimeRaw()
|
||||||
self.check_clock(_testcapi.PyTime_Time, time.time)
|
self.check_clock(_testcapi.PyTime_Time, time.time)
|
||||||
|
self.check_clock(_testcapi.PyTime_TimeRaw, time.time)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
Add "Raw" variant of PyTime functions
|
||||||
|
|
||||||
|
* :c:func:`PyTime_MonotonicRaw`
|
||||||
|
* :c:func:`PyTime_PerfCounterRaw`
|
||||||
|
* :c:func:`PyTime_TimeRaw`
|
||||||
|
|
||||||
|
Patch by Victor Stinner.
|
|
@ -51,6 +51,25 @@ test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
PyTime_t t;
|
PyTime_t t;
|
||||||
int res = PyTime_Monotonic(&t);
|
int res = PyTime_Monotonic(&t);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(res == 0);
|
||||||
|
return pytime_as_float(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_pytime_monotonic_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyTime_t t;
|
||||||
|
int res;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
res = PyTime_MonotonicRaw(&t);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "PyTime_MonotonicRaw() failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
assert(res == 0);
|
assert(res == 0);
|
||||||
|
@ -64,6 +83,25 @@ test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
PyTime_t t;
|
PyTime_t t;
|
||||||
int res = PyTime_PerfCounter(&t);
|
int res = PyTime_PerfCounter(&t);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(res == 0);
|
||||||
|
return pytime_as_float(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_pytime_perf_counter_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyTime_t t;
|
||||||
|
int res;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
res = PyTime_PerfCounterRaw(&t);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "PyTime_PerfCounterRaw() failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
assert(res == 0);
|
assert(res == 0);
|
||||||
|
@ -77,6 +115,25 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
PyTime_t t;
|
PyTime_t t;
|
||||||
int res = PyTime_Time(&t);
|
int res = PyTime_Time(&t);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(res == 0);
|
||||||
|
return pytime_as_float(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_pytime_time_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyTime_t t;
|
||||||
|
int res;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
res = PyTime_TimeRaw(&t);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (res < 0) {
|
||||||
|
assert(t == 0);
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "PyTime_TimeRaw() failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
assert(res == 0);
|
assert(res == 0);
|
||||||
|
@ -87,8 +144,11 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
|
{"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
|
||||||
{"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS},
|
{"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS},
|
||||||
|
{"PyTime_MonotonicRaw", test_pytime_monotonic_raw, METH_NOARGS},
|
||||||
{"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS},
|
{"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS},
|
||||||
|
{"PyTime_PerfCounterRaw", test_pytime_perf_counter_raw, METH_NOARGS},
|
||||||
{"PyTime_Time", test_pytime_time, METH_NOARGS},
|
{"PyTime_Time", test_pytime_time, METH_NOARGS},
|
||||||
|
{"PyTime_TimeRaw", test_pytime_time_raw, METH_NOARGS},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
109
Python/pytime.c
109
Python/pytime.c
|
@ -898,6 +898,10 @@ static int
|
||||||
py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||||
{
|
{
|
||||||
assert(info == NULL || raise_exc);
|
assert(info == NULL || raise_exc);
|
||||||
|
if (raise_exc) {
|
||||||
|
// raise_exc requires to hold the GIL
|
||||||
|
assert(PyGILState_Check());
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
FILETIME system_time;
|
FILETIME system_time;
|
||||||
|
@ -1004,19 +1008,6 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyTime_t
|
|
||||||
_PyTime_TimeUnchecked(void)
|
|
||||||
{
|
|
||||||
PyTime_t t;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
PyTime_Time(PyTime_t *result)
|
PyTime_Time(PyTime_t *result)
|
||||||
{
|
{
|
||||||
|
@ -1027,6 +1018,34 @@ PyTime_Time(PyTime_t *result)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyTime_TimeRaw(PyTime_t *result)
|
||||||
|
{
|
||||||
|
if (py_get_system_clock(result, NULL, 0) < 0) {
|
||||||
|
*result = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyTime_t
|
||||||
|
_PyTime_TimeUnchecked(void)
|
||||||
|
{
|
||||||
|
PyTime_t t;
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
int result = PyTime_TimeRaw(&t);
|
||||||
|
if (result != 0) {
|
||||||
|
Py_FatalError("unable to read the system clock");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)PyTime_TimeRaw(&t);
|
||||||
|
#endif
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
|
_PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
|
@ -1140,6 +1159,10 @@ static int
|
||||||
py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||||
{
|
{
|
||||||
assert(info == NULL || raise_exc);
|
assert(info == NULL || raise_exc);
|
||||||
|
if (raise_exc) {
|
||||||
|
// raise_exc requires to hold the GIL
|
||||||
|
assert(PyGILState_Check());
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(MS_WINDOWS)
|
#if defined(MS_WINDOWS)
|
||||||
if (py_get_win_perf_counter(tp, info, raise_exc) < 0) {
|
if (py_get_win_perf_counter(tp, info, raise_exc) < 0) {
|
||||||
|
@ -1225,18 +1248,6 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyTime_t
|
|
||||||
_PyTime_MonotonicUnchecked(void)
|
|
||||||
{
|
|
||||||
PyTime_t t;
|
|
||||||
if (py_get_monotonic_clock(&t, NULL, 0) < 0) {
|
|
||||||
// Ignore silently the error and return 0.
|
|
||||||
t = 0;
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
PyTime_Monotonic(PyTime_t *result)
|
PyTime_Monotonic(PyTime_t *result)
|
||||||
{
|
{
|
||||||
|
@ -1248,6 +1259,33 @@ PyTime_Monotonic(PyTime_t *result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyTime_MonotonicRaw(PyTime_t *result)
|
||||||
|
{
|
||||||
|
if (py_get_monotonic_clock(result, NULL, 0) < 0) {
|
||||||
|
*result = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyTime_t
|
||||||
|
_PyTime_MonotonicUnchecked(void)
|
||||||
|
{
|
||||||
|
PyTime_t t;
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
int result = PyTime_MonotonicRaw(&t);
|
||||||
|
if (result != 0) {
|
||||||
|
Py_FatalError("unable to read the monotonic clock");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)PyTime_MonotonicRaw(&t);
|
||||||
|
#endif
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_MonotonicWithInfo(PyTime_t *tp, _Py_clock_info_t *info)
|
_PyTime_MonotonicWithInfo(PyTime_t *tp, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
|
@ -1262,13 +1300,6 @@ _PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyTime_t
|
|
||||||
_PyTime_PerfCounterUnchecked(void)
|
|
||||||
{
|
|
||||||
return _PyTime_MonotonicUnchecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
PyTime_PerfCounter(PyTime_t *result)
|
PyTime_PerfCounter(PyTime_t *result)
|
||||||
{
|
{
|
||||||
|
@ -1276,6 +1307,20 @@ PyTime_PerfCounter(PyTime_t *result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyTime_PerfCounterRaw(PyTime_t *result)
|
||||||
|
{
|
||||||
|
return PyTime_MonotonicRaw(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyTime_t
|
||||||
|
_PyTime_PerfCounterUnchecked(void)
|
||||||
|
{
|
||||||
|
return _PyTime_MonotonicUnchecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_localtime(time_t t, struct tm *tm)
|
_PyTime_localtime(time_t t, struct tm *tm)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue