diff --git a/Doc/c-api/time.rst b/Doc/c-api/time.rst index 7791cdb1781055..1dffc19fdf0a8c 100644 --- a/Doc/c-api/time.rst +++ b/Doc/c-api/time.rst @@ -61,16 +61,40 @@ with the :term:`GIL` held. Read the monotonic clock. See :func:`time.monotonic` for important details on this clock. +.. c:function:: PyTime_t PyTime_MonotonicUnchecked() + + Similar to :c:func:`PyTime_Monotonic()`, but return ``0`` and silently + ignore the error if reading the clock fails. + + The caller doesn't have to hold the GIL; the function does not raise an + exception on error. + .. 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:: PyTime_t PyTime_PerfCounterUnchecked() + + Similar to :c:func:`PyTime_PerfCounter()`, but return ``0`` and silently + ignore the error if reading the clock fails. + + The caller doesn't have to hold the GIL; the function does not raise an + exception on error. + .. c:function:: int PyTime_Time(PyTime_t *result) Read the “wall clock” time. See :func:`time.time` for details important on this clock. +.. c:function:: PyTime_t PyTime_TimeUnchecked() + + Similar to :c:func:`PyTime_Time()`, but return ``0`` and silently ignore the + error if reading the clock fails. + + The caller doesn't have to hold the GIL; the function does not raise an + exception on error. + Conversion functions -------------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index a393a1df71a65c..7e228b1d712fbe 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1571,6 +1571,9 @@ New Features * :c:func:`PyTime_AsSecondsDouble` :c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and :c:func:`PyTime_Time` functions. + * :c:func:`PyTime_MonotonicUnchecked`, :c:func:`PyTime_PerfCounterUnchecked` + and :c:func:`PyTime_TimeUnchecked`: similar to previous functions, but + ignores errors and doesn't require to hold the GIL. (Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.) diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h index d8244700d614ce..f70e59cc56fadf 100644 --- a/Include/cpython/pytime.h +++ b/Include/cpython/pytime.h @@ -15,6 +15,9 @@ 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); +PyAPI_FUNC(PyTime_t) PyTime_MonotonicUnchecked(void); +PyAPI_FUNC(PyTime_t) PyTime_PerfCounterUnchecked(void); +PyAPI_FUNC(PyTime_t) PyTime_TimeUnchecked(void); #ifdef __cplusplus } diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h index 40b28e0ba221ae..ced491227b3604 100644 --- a/Include/internal/pycore_time.h +++ b/Include/internal/pycore_time.h @@ -247,13 +247,10 @@ typedef struct { double resolution; } _Py_clock_info_t; -// Similar to PyTime_Time() but silently ignore the error and return 0 if the -// internal clock fails. -// -// Use _PyTime_TimeWithInfo() or the public PyTime_Time() to check -// for failure. -// Export for '_random' shared extension. -PyAPI_FUNC(PyTime_t) _PyTime_TimeUnchecked(void); +// Backward compatibility +#define _PyTime_TimeUnchecked PyTime_TimeUnchecked +#define _PyTime_MonotonicUnchecked PyTime_MonotonicUnchecked +#define _PyTime_PerfCounterUnchecked PyTime_PerfCounterUnchecked // Get the current time from the system clock. // On success, set *t and *info (if not NULL), and return 0. @@ -262,14 +259,6 @@ extern int _PyTime_TimeWithInfo( PyTime_t *t, _Py_clock_info_t *info); -// Similar to PyTime_Monotonic() but silently ignore the error and return 0 if -// the internal clock fails. -// -// Use _PyTime_MonotonicWithInfo() or the public PyTime_Monotonic() -// to check for failure. -// Export for '_random' shared extension. -PyAPI_FUNC(PyTime_t) _PyTime_MonotonicUnchecked(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 @@ -294,14 +283,6 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm); // Export for '_datetime' shared extension. PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm); -// Similar to PyTime_PerfCounter() but silently ignore the error and return 0 -// if the internal clock fails. -// -// Use _PyTime_PerfCounterWithInfo() or the public PyTime_PerfCounter() to -// check for failure. -// Export for '_lsprof' shared extension. -PyAPI_FUNC(PyTime_t) _PyTime_PerfCounterUnchecked(void); - // Get the performance counter: clock with the highest available resolution to // measure a short duration. @@ -353,6 +334,8 @@ extern double _PyTimeFraction_Resolution( const _PyTimeFraction *frac); +extern PyStatus _PyTime_Init(void); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_capi/test_time.py b/Lib/test/test_capi/test_time.py index 10b7fbf2c372a3..f0c5a437f594f1 100644 --- a/Lib/test/test_capi/test_time.py +++ b/Lib/test/test_capi/test_time.py @@ -8,7 +8,7 @@ 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. +# Worst clock resolution in seconds: maximum delta between two clock reads. CLOCK_RES = 0.050 @@ -59,13 +59,16 @@ def ns_to_sec(ns): ns_to_sec(ns)) def test_monotonic(self): - # Test PyTime_Monotonic() + # Test PyTime_Monotonic() and PyTime_MonotonicUnchecked() self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic) + self.check_clock(_testcapi.PyTime_MonotonicUnchecked, time.monotonic) def test_perf_counter(self): - # Test PyTime_PerfCounter() + # Test PyTime_PerfCounter() and PyTime_PerfCounterUnchecked() self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter) + self.check_clock(_testcapi.PyTime_PerfCounterUnchecked, time.perf_counter) def test_time(self): - # Test PyTime_time() + # Test PyTime_Time() and PyTime_TimeUnchecked() self.check_clock(_testcapi.PyTime_Time, time.time) + self.check_clock(_testcapi.PyTime_TimeUnchecked, time.time) diff --git a/Misc/NEWS.d/next/C API/2024-02-26-22-23-24.gh-issue-110850.nlLqWV.rst b/Misc/NEWS.d/next/C API/2024-02-26-22-23-24.gh-issue-110850.nlLqWV.rst new file mode 100644 index 00000000000000..9fee20c0f69687 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-02-26-22-23-24.gh-issue-110850.nlLqWV.rst @@ -0,0 +1,7 @@ +Add functions: + +* :c:func:`PyTime_MonotonicUnchecked` +* :c:func:`PyTime_PerfCounterUnchecked` +* :c:func:`PyTime_TimeUnchecked` + +Patch by Victor Stinner. diff --git a/Modules/_testcapi/time.c b/Modules/_testcapi/time.c index 68f082bf3f3d88..fd33fa6bbe0e12 100644 --- a/Modules/_testcapi/time.c +++ b/Modules/_testcapi/time.c @@ -58,6 +58,14 @@ test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) } +static PyObject* +test_pytime_monotonic_unchecked(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t = PyTime_MonotonicUnchecked(); + return pytime_as_float(t); +} + + static PyObject* test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { @@ -71,6 +79,14 @@ test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) } +static PyObject* +test_pytime_perf_counter_unchecked(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t = PyTime_PerfCounterUnchecked(); + return pytime_as_float(t); +} + + static PyObject* test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { @@ -84,11 +100,22 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) } +static PyObject* +test_pytime_time_unchecked(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyTime_t t = PyTime_TimeUnchecked(); + return pytime_as_float(t); +} + + static PyMethodDef test_methods[] = { {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS}, {"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS}, + {"PyTime_MonotonicUnchecked", test_pytime_monotonic_unchecked, METH_NOARGS}, {"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS}, + {"PyTime_PerfCounterUnchecked", test_pytime_perf_counter_unchecked, METH_NOARGS}, {"PyTime_Time", test_pytime_time, METH_NOARGS}, + {"PyTime_TimeUnchecked", test_pytime_time_unchecked, METH_NOARGS}, {NULL}, }; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 04487345f7ec05..a875790d35b318 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -27,6 +27,7 @@ #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_sliceobject.h" // _PySlice_Fini() #include "pycore_sysmodule.h" // _PySys_ClearAuditHooks() +#include "pycore_time.h" // _PyTime_Init() #include "pycore_traceback.h" // _Py_DumpTracebackThreads() #include "pycore_typeobject.h" // _PyTypes_InitTypes() #include "pycore_typevarobject.h" // _Py_clear_generic_types() @@ -525,6 +526,11 @@ pycore_init_runtime(_PyRuntimeState *runtime, _Py_InitVersion(); + status = _PyTime_Init(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + status = _Py_HashRandomization_Init(config); if (_PyStatus_EXCEPTION(status)) { return status; diff --git a/Python/pytime.c b/Python/pytime.c index 90ef2eeb546f7f..5ff57eb8fa5a8e 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_initconfig.h" // _PyStatus_ERR() #include "pycore_time.h" // PyTime_t #include // gmtime_r() @@ -1289,28 +1290,33 @@ py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) #endif // MS_WINDOWS -int -_PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info) +static int +py_perf_counter(PyTime_t *result, _Py_clock_info_t *info, int raise_exc) { #ifdef MS_WINDOWS - return py_get_win_perf_counter(t, info, 1); +# define INIT_CHECK_PERF_COUNTER 1 + return py_get_win_perf_counter(result, info, raise_exc); #else - return _PyTime_MonotonicWithInfo(t, info); + // On non-Windows, _PyTime_Init() doesn't have to check py_perf_counter() + // since py_get_monotonic_clock() is already checked. +# define INIT_CHECK_PERF_COUNTER 0 + return py_get_monotonic_clock(result, info, raise_exc); #endif } +int +_PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info) +{ + return py_perf_counter(t, info, 1); +} + + PyTime_t _PyTime_PerfCounterUnchecked(void) { PyTime_t t; - 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 (py_perf_counter(&t, NULL, 0) < 0) { // If py_win_perf_counter_frequency() or py_get_monotonic_clock() // fails: silently ignore the failure and return 0. t = 0; @@ -1322,13 +1328,7 @@ _PyTime_PerfCounterUnchecked(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_perf_counter(result, NULL, 1) < 0) { // If py_win_perf_counter_frequency() or py_get_monotonic_clock() // fails: silently ignore the failure and return 0. *result = 0; @@ -1419,3 +1419,22 @@ _PyDeadline_Get(PyTime_t deadline) PyTime_t now = _PyTime_MonotonicUnchecked(); return deadline - now; } + + +PyStatus +_PyTime_Init(void) +{ + PyTime_t t; + if (py_get_system_clock(&t, NULL, 0) < 0) { + return _PyStatus_ERR("cannot read system clock"); + } + if (py_get_monotonic_clock(&t, NULL, 0) < 0) { + return _PyStatus_ERR("cannot read monotonic clock"); + } +#if INIT_CHECK_PERF_COUNTER + if (py_perf_counter(&t, NULL, 0) < 0) { + return _PyStatus_ERR("cannot read monotonic clock"); + } +#endif + return _PyStatus_OK(); +}