Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b52c753

Browse files
vstinnerencukou
andauthored
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 <[email protected]>
1 parent a8bcf3e commit b52c753

File tree

7 files changed

+189
-35
lines changed

7 files changed

+189
-35
lines changed

Doc/c-api/time.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,35 @@ with the :term:`GIL` held.
7272
See :func:`time.time` for details important on this clock.
7373
7474
75+
Raw Clock Functions
76+
-------------------
77+
78+
Similar to clock functions, but don't set an exception on error and don't
79+
require the caller to hold the GIL.
80+
81+
On success, the functions return ``0``.
82+
83+
On failure, they set ``*result`` to ``0`` and return ``-1``, *without* setting
84+
an exception. To get the cause of the error, acquire the GIL and call the
85+
regular (non-``Raw``) function. Note that the regular function may succeed after
86+
the ``Raw`` one failed.
87+
88+
.. c:function:: int PyTime_MonotonicRaw(PyTime_t *result)
89+
90+
Similar to :c:func:`PyTime_Monotonic`,
91+
but don't set an exception on error and don't require holding the GIL.
92+
93+
.. c:function:: int PyTime_PerfCounterRaw(PyTime_t *result)
94+
95+
Similar to :c:func:`PyTime_PerfCounter`,
96+
but don't set an exception on error and don't require holding the GIL.
97+
98+
.. c:function:: int PyTime_TimeRaw(PyTime_t *result)
99+
100+
Similar to :c:func:`PyTime_Time`,
101+
but don't set an exception on error and don't require holding the GIL.
102+
103+
75104
Conversion functions
76105
--------------------
77106

Doc/whatsnew/3.13.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,9 +1901,15 @@ New Features
19011901

19021902
* :c:type:`PyTime_t` type.
19031903
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
1904-
* :c:func:`PyTime_AsSecondsDouble`
1905-
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
1906-
:c:func:`PyTime_Time` functions.
1904+
* Add functions:
1905+
1906+
* :c:func:`PyTime_AsSecondsDouble`.
1907+
* :c:func:`PyTime_Monotonic`.
1908+
* :c:func:`PyTime_MonotonicRaw`.
1909+
* :c:func:`PyTime_PerfCounter`.
1910+
* :c:func:`PyTime_PerfCounterRaw`.
1911+
* :c:func:`PyTime_Time`.
1912+
* :c:func:`PyTime_TimeRaw`.
19071913

19081914
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
19091915

Include/cpython/pytime.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ PyAPI_FUNC(int) PyTime_Monotonic(PyTime_t *result);
1616
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
1717
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);
1818

19+
PyAPI_FUNC(int) PyTime_MonotonicRaw(PyTime_t *result);
20+
PyAPI_FUNC(int) PyTime_PerfCounterRaw(PyTime_t *result);
21+
PyAPI_FUNC(int) PyTime_TimeRaw(PyTime_t *result);
22+
1923
#ifdef __cplusplus
2024
}
2125
#endif

Lib/test/test_capi/test_time.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ def test_min_max(self):
1818
self.assertEqual(PyTime_MIN, -2**63)
1919
self.assertEqual(PyTime_MAX, 2**63 - 1)
2020

21-
def check_clock(self, c_func, py_func):
22-
t1 = c_func()
23-
t2 = py_func()
24-
self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
25-
2621
def test_assecondsdouble(self):
2722
# Test PyTime_AsSecondsDouble()
2823
def ns_to_sec(ns):
@@ -58,14 +53,22 @@ def ns_to_sec(ns):
5853
self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
5954
ns_to_sec(ns))
6055

56+
def check_clock(self, c_func, py_func):
57+
t1 = c_func()
58+
t2 = py_func()
59+
self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
60+
6161
def test_monotonic(self):
62-
# Test PyTime_Monotonic()
62+
# Test PyTime_Monotonic() and PyTime_MonotonicRaw()
6363
self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
64+
self.check_clock(_testcapi.PyTime_MonotonicRaw, time.monotonic)
6465

6566
def test_perf_counter(self):
66-
# Test PyTime_PerfCounter()
67+
# Test PyTime_PerfCounter() and PyTime_PerfCounterRaw()
6768
self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
69+
self.check_clock(_testcapi.PyTime_PerfCounterRaw, time.perf_counter)
6870

6971
def test_time(self):
70-
# Test PyTime_time()
72+
# Test PyTime_Time() and PyTime_TimeRaw()
7173
self.check_clock(_testcapi.PyTime_Time, time.time)
74+
self.check_clock(_testcapi.PyTime_TimeRaw, time.time)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Add "Raw" variant of PyTime functions
2+
3+
* :c:func:`PyTime_MonotonicRaw`
4+
* :c:func:`PyTime_PerfCounterRaw`
5+
* :c:func:`PyTime_TimeRaw`
6+
7+
Patch by Victor Stinner.

Modules/_testcapi/time.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
5151
PyTime_t t;
5252
int res = PyTime_Monotonic(&t);
5353
if (res < 0) {
54+
assert(t == 0);
55+
return NULL;
56+
}
57+
assert(res == 0);
58+
return pytime_as_float(t);
59+
}
60+
61+
62+
static PyObject*
63+
test_pytime_monotonic_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
64+
{
65+
PyTime_t t;
66+
int res;
67+
Py_BEGIN_ALLOW_THREADS
68+
res = PyTime_MonotonicRaw(&t);
69+
Py_END_ALLOW_THREADS
70+
if (res < 0) {
71+
assert(t == 0);
72+
PyErr_SetString(PyExc_RuntimeError, "PyTime_MonotonicRaw() failed");
5473
return NULL;
5574
}
5675
assert(res == 0);
@@ -64,6 +83,25 @@ test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
6483
PyTime_t t;
6584
int res = PyTime_PerfCounter(&t);
6685
if (res < 0) {
86+
assert(t == 0);
87+
return NULL;
88+
}
89+
assert(res == 0);
90+
return pytime_as_float(t);
91+
}
92+
93+
94+
static PyObject*
95+
test_pytime_perf_counter_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
96+
{
97+
PyTime_t t;
98+
int res;
99+
Py_BEGIN_ALLOW_THREADS
100+
res = PyTime_PerfCounterRaw(&t);
101+
Py_END_ALLOW_THREADS
102+
if (res < 0) {
103+
assert(t == 0);
104+
PyErr_SetString(PyExc_RuntimeError, "PyTime_PerfCounterRaw() failed");
67105
return NULL;
68106
}
69107
assert(res == 0);
@@ -77,6 +115,25 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
77115
PyTime_t t;
78116
int res = PyTime_Time(&t);
79117
if (res < 0) {
118+
assert(t == 0);
119+
return NULL;
120+
}
121+
assert(res == 0);
122+
return pytime_as_float(t);
123+
}
124+
125+
126+
static PyObject*
127+
test_pytime_time_raw(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
128+
{
129+
PyTime_t t;
130+
int res;
131+
Py_BEGIN_ALLOW_THREADS
132+
res = PyTime_TimeRaw(&t);
133+
Py_END_ALLOW_THREADS
134+
if (res < 0) {
135+
assert(t == 0);
136+
PyErr_SetString(PyExc_RuntimeError, "PyTime_TimeRaw() failed");
80137
return NULL;
81138
}
82139
assert(res == 0);
@@ -87,8 +144,11 @@ test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
87144
static PyMethodDef test_methods[] = {
88145
{"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
89146
{"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS},
147+
{"PyTime_MonotonicRaw", test_pytime_monotonic_raw, METH_NOARGS},
90148
{"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS},
149+
{"PyTime_PerfCounterRaw", test_pytime_perf_counter_raw, METH_NOARGS},
91150
{"PyTime_Time", test_pytime_time, METH_NOARGS},
151+
{"PyTime_TimeRaw", test_pytime_time_raw, METH_NOARGS},
92152
{NULL},
93153
};
94154

Python/pytime.c

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,10 @@ static int
898898
py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
899899
{
900900
assert(info == NULL || raise_exc);
901+
if (raise_exc) {
902+
// raise_exc requires to hold the GIL
903+
assert(PyGILState_Check());
904+
}
901905

902906
#ifdef MS_WINDOWS
903907
FILETIME system_time;
@@ -1004,29 +1008,44 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
10041008
}
10051009

10061010

1007-
PyTime_t
1008-
_PyTime_TimeUnchecked(void)
1011+
int
1012+
PyTime_Time(PyTime_t *result)
10091013
{
1010-
PyTime_t t;
1011-
if (py_get_system_clock(&t, NULL, 0) < 0) {
1012-
// If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails:
1013-
// silently ignore the failure and return 0.
1014-
t = 0;
1014+
if (py_get_system_clock(result, NULL, 1) < 0) {
1015+
*result = 0;
1016+
return -1;
10151017
}
1016-
return t;
1018+
return 0;
10171019
}
10181020

10191021

10201022
int
1021-
PyTime_Time(PyTime_t *result)
1023+
PyTime_TimeRaw(PyTime_t *result)
10221024
{
1023-
if (py_get_system_clock(result, NULL, 1) < 0) {
1025+
if (py_get_system_clock(result, NULL, 0) < 0) {
10241026
*result = 0;
10251027
return -1;
10261028
}
10271029
return 0;
10281030
}
10291031

1032+
1033+
PyTime_t
1034+
_PyTime_TimeUnchecked(void)
1035+
{
1036+
PyTime_t t;
1037+
#ifdef Py_DEBUG
1038+
int result = PyTime_TimeRaw(&t);
1039+
if (result != 0) {
1040+
Py_FatalError("unable to read the system clock");
1041+
}
1042+
#else
1043+
(void)PyTime_TimeRaw(&t);
1044+
#endif
1045+
return t;
1046+
}
1047+
1048+
10301049
int
10311050
_PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
10321051
{
@@ -1140,6 +1159,10 @@ static int
11401159
py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
11411160
{
11421161
assert(info == NULL || raise_exc);
1162+
if (raise_exc) {
1163+
// raise_exc requires to hold the GIL
1164+
assert(PyGILState_Check());
1165+
}
11431166

11441167
#if defined(MS_WINDOWS)
11451168
if (py_get_win_perf_counter(tp, info, raise_exc) < 0) {
@@ -1225,29 +1248,44 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
12251248
}
12261249

12271250

1228-
PyTime_t
1229-
_PyTime_MonotonicUnchecked(void)
1251+
int
1252+
PyTime_Monotonic(PyTime_t *result)
12301253
{
1231-
PyTime_t t;
1232-
if (py_get_monotonic_clock(&t, NULL, 0) < 0) {
1233-
// Ignore silently the error and return 0.
1234-
t = 0;
1254+
if (py_get_monotonic_clock(result, NULL, 1) < 0) {
1255+
*result = 0;
1256+
return -1;
12351257
}
1236-
return t;
1258+
return 0;
12371259
}
12381260

12391261

12401262
int
1241-
PyTime_Monotonic(PyTime_t *result)
1263+
PyTime_MonotonicRaw(PyTime_t *result)
12421264
{
1243-
if (py_get_monotonic_clock(result, NULL, 1) < 0) {
1265+
if (py_get_monotonic_clock(result, NULL, 0) < 0) {
12441266
*result = 0;
12451267
return -1;
12461268
}
12471269
return 0;
12481270
}
12491271

12501272

1273+
PyTime_t
1274+
_PyTime_MonotonicUnchecked(void)
1275+
{
1276+
PyTime_t t;
1277+
#ifdef Py_DEBUG
1278+
int result = PyTime_MonotonicRaw(&t);
1279+
if (result != 0) {
1280+
Py_FatalError("unable to read the monotonic clock");
1281+
}
1282+
#else
1283+
(void)PyTime_MonotonicRaw(&t);
1284+
#endif
1285+
return t;
1286+
}
1287+
1288+
12511289
int
12521290
_PyTime_MonotonicWithInfo(PyTime_t *tp, _Py_clock_info_t *info)
12531291
{
@@ -1262,17 +1300,24 @@ _PyTime_PerfCounterWithInfo(PyTime_t *t, _Py_clock_info_t *info)
12621300
}
12631301

12641302

1265-
PyTime_t
1266-
_PyTime_PerfCounterUnchecked(void)
1303+
int
1304+
PyTime_PerfCounter(PyTime_t *result)
12671305
{
1268-
return _PyTime_MonotonicUnchecked();
1306+
return PyTime_Monotonic(result);
12691307
}
12701308

12711309

12721310
int
1273-
PyTime_PerfCounter(PyTime_t *result)
1311+
PyTime_PerfCounterRaw(PyTime_t *result)
12741312
{
1275-
return PyTime_Monotonic(result);
1313+
return PyTime_MonotonicRaw(result);
1314+
}
1315+
1316+
1317+
PyTime_t
1318+
_PyTime_PerfCounterUnchecked(void)
1319+
{
1320+
return _PyTime_MonotonicUnchecked();
12761321
}
12771322

12781323

0 commit comments

Comments
 (0)