Skip to content

Commit e9a6c02

Browse files
committed
gh-110850: Add PyTime_t C API
Add PyTime_t API: * PyTime_t type. * PyTime_MIN and PyTime_MAX constants. * PyTime_AsSecondsDouble(), PyTime_GetMonotonicClock(), PyTime_GetPerfCounter() and PyTime_GetSystemClock() functions.
1 parent a646560 commit e9a6c02

23 files changed

+270
-190
lines changed

Doc/c-api/time.rst

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
.. highlight:: c
2+
3+
PyTime C API
4+
============
5+
6+
.. versionadded:: 3.13
7+
8+
PyTime API
9+
----------
10+
11+
The PyTime_t API is written to use timestamp and timeout values stored in
12+
various formats and to read clocks.
13+
14+
The :c:type:`PyTime_t` type is an integer to support directly common arithmetic
15+
operations such as ``t1 + t2``.
16+
17+
The PyTime_t API supports a resolution of ``1`` nanosecond. The
18+
:c:type:`PyTime_t` type is signed to support negative timestamps. The supported
19+
range is around [-292.3 years; +292.3 years]. Using the Unix epoch (January
20+
1st, 1970), the supported date range is around [1677-09-21; 2262-04-11].
21+
22+
Time formats:
23+
24+
* Seconds.
25+
* Seconds as a floating pointer number (C :c:type:`double`).
26+
* Milliseconds (10\ :sup:`-3` seconds).
27+
* Microseconds (10\ :sup:`-6` seconds).
28+
* 100 nanoseconds (10\ :sup:`-7` seconds), used on Windows.
29+
* Nanoseconds (10\ :sup:`-9` seconds).
30+
* :c:expr:`timeval` structure, 1 microsecond (10\ :sup:`-6` seconds).
31+
* :c:expr:`timespec` structure, 1 nanosecond (10\ :sup:`-9` seconds).
32+
33+
Integer overflows are detected and raise :exc:`OverflowError`. Conversion to a
34+
resolution larger than 1 nanosecond is rounded correctly with the requested
35+
rounding mode. Available rounding modes:
36+
37+
* Round towards minus infinity (-inf). For example, used to read a clock.
38+
* Round towards infinity (+inf). For example, used for timeout to wait "at
39+
least" N seconds.
40+
* Round to nearest with ties going to nearest even integer. For example, used
41+
to round from a Python float.
42+
* Round away from zero. For example, used for timeout.
43+
44+
Some functions clamp the result in the range [PyTime_MIN; PyTime_MAX]. The
45+
caller doesn't have to handle errors and so doesn't need to hold the GIL to
46+
handle exceptions. For example, ``_PyTime_Add(t1, t2)`` computes ``t1+t2`` and
47+
clamps the result on overflow.
48+
49+
Clocks:
50+
51+
* System clock
52+
* Monotonic clock
53+
* Performance counter
54+
55+
Internally, operations like ``(t * k / q)`` with integers are implemented in a
56+
way to reduce the risk of integer overflow. Such operation is used to convert a
57+
clock value expressed in ticks with a frequency to PyTime_t, like
58+
``QueryPerformanceCounter()`` with ``QueryPerformanceFrequency()`` on Windows.
59+
60+
61+
Types
62+
-----
63+
64+
.. c:type:: PyTime_t
65+
66+
Timestamp type with subsecond precision: 64-bit signed integer.
67+
68+
This type can be used to store a duration. Indirectly, it can be used to
69+
store a date relative to a reference date, such as the UNIX epoch.
70+
71+
72+
Constants
73+
---------
74+
75+
.. c:var:: PyTime_t PyTime_MIN
76+
77+
Minimum value of the :c:type:`PyTime_t` type.
78+
:c:macro`PyTime_MIN` nanoseconds is around -292.3 years.
79+
80+
.. c:var:: PyTime_t PyTime_MAX
81+
82+
Maximum value of the :c:type:`PyTime_t` type.
83+
:c:macro`PyTime_MAX` nanoseconds is around +292.3 years.
84+
85+
86+
Functions
87+
---------
88+
89+
.. c:function:: double PyTime_AsSecondsDouble(PyTime_t t)
90+
91+
Convert a timestamp to a number of seconds as a C :c:type:`double`.
92+
93+
The function cannot fail.
94+
95+
96+
.. c:function:: PyTime_t PyTime_GetMonotonicClock(void)
97+
98+
Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
99+
The clock is not affected by system clock updates. The reference point of
100+
the returned value is undefined, so that only the difference between the
101+
results of consecutive calls is valid.
102+
103+
If reading the clock fails, silently ignore the error and return 0.
104+
105+
On integer overflow, silently ignore the overflow and clamp the clock to
106+
the [PyTime_MIN; PyTime_MAX] range.
107+
108+
See also the :func:`time.monotonic` function.
109+
110+
.. c:function:: PyTime_t PyTime_GetPerfCounter(void)
111+
112+
Get the performance counter: clock with the highest available resolution to
113+
measure a short duration.
114+
115+
If reading the clock fails, silently ignore the error and return 0.
116+
117+
On integer overflow, silently ignore the overflow and clamp the time to the
118+
[PyTime_MIN; PyTime_MAX] range.
119+
120+
See also the :func:`time.perf_counter` function.
121+
122+
123+
.. c:function:: PyTime_t PyTime_GetSystemClock(void)
124+
125+
Get the current time from the system clock.
126+
127+
If reading the clock fails, silently ignore the error and return ``0``.
128+
129+
On integer overflow, silently ignore the overflow and clamp the clock to
130+
the [PyTime_MIN; PyTime_MAX] range.
131+
132+
See also the :func:`time.time` function.

Doc/c-api/utilities.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ and parsing function arguments and constructing Python values from C values.
2020
hash.rst
2121
reflection.rst
2222
codec.rst
23+
time.rst
2324
perfmaps.rst

Doc/whatsnew/3.13.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,16 @@ New Features
11811181
:exc:`KeyError` if the key missing.
11821182
(Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.)
11831183

1184+
* Add PyTime_t C API:
1185+
1186+
* :c:type:`PyTime_t` type.
1187+
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
1188+
* :c:func:`PyTime_AsSecondsDouble`, :c:func:`PyTime_GetMonotonicClock`,
1189+
:c:func:`PyTime_GetPerfCounter` and :c:func:`PyTime_GetSystemClock`
1190+
functions.
1191+
1192+
(Contributed by Victor Stinner in :gh:`110850`.)
1193+
11841194

11851195
Porting to Python 3.13
11861196
----------------------

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#include "weakrefobject.h"
9898
#include "structseq.h"
9999
#include "cpython/picklebufobject.h"
100+
#include "cpython/pytime.h"
100101
#include "codecs.h"
101102
#include "pyerrors.h"
102103
#include "pythread.h"

Include/cpython/pytime.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// PyTime_t C API: see Doc/c-api/time.rst for the documentation.
2+
3+
#ifndef Py_LIMITED_API
4+
#ifndef Py_PYTIME_H
5+
#define Py_PYTIME_H
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
typedef int64_t PyTime_t;
11+
#define PyTime_MIN INT64_MIN
12+
#define PyTime_MAX INT64_MAX
13+
14+
PyAPI_FUNC(double) PyTime_AsSecondsDouble(PyTime_t t);
15+
PyAPI_FUNC(PyTime_t) PyTime_GetMonotonicClock(void);
16+
PyAPI_FUNC(PyTime_t) PyTime_GetPerfCounter(void);
17+
PyAPI_FUNC(PyTime_t) PyTime_GetSystemClock(void);
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
#endif /* Py_PYTIME_H */
23+
#endif /* Py_LIMITED_API */

Include/internal/pycore_time.h

Lines changed: 7 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,4 @@
1-
// The _PyTime_t API is written to use timestamp and timeout values stored in
2-
// various formats and to read clocks.
3-
//
4-
// The _PyTime_t type is an integer to support directly common arithmetic
5-
// operations like t1 + t2.
6-
//
7-
// The _PyTime_t API supports a resolution of 1 nanosecond. The _PyTime_t type
8-
// is signed to support negative timestamps. The supported range is around
9-
// [-292.3 years; +292.3 years]. Using the Unix epoch (January 1st, 1970), the
10-
// supported date range is around [1677-09-21; 2262-04-11].
11-
//
12-
// Formats:
13-
//
14-
// * seconds
15-
// * seconds as a floating pointer number (C double)
16-
// * milliseconds (10^-3 seconds)
17-
// * microseconds (10^-6 seconds)
18-
// * 100 nanoseconds (10^-7 seconds)
19-
// * nanoseconds (10^-9 seconds)
20-
// * timeval structure, 1 microsecond resolution (10^-6 seconds)
21-
// * timespec structure, 1 nanosecond resolution (10^-9 seconds)
22-
//
23-
// Integer overflows are detected and raise OverflowError. Conversion to a
24-
// resolution worse than 1 nanosecond is rounded correctly with the requested
25-
// rounding mode. There are 4 rounding modes: floor (towards -inf), ceiling
26-
// (towards +inf), half even and up (away from zero).
27-
//
28-
// Some functions clamp the result in the range [_PyTime_MIN; _PyTime_MAX], so
29-
// the caller doesn't have to handle errors and doesn't need to hold the GIL.
30-
// For example, _PyTime_Add(t1, t2) computes t1+t2 and clamp the result on
31-
// overflow.
32-
//
33-
// Clocks:
34-
//
35-
// * System clock
36-
// * Monotonic clock
37-
// * Performance counter
38-
//
39-
// Operations like (t * k / q) with integers are implemented in a way to reduce
40-
// the risk of integer overflow. Such operation is used to convert a clock
41-
// value expressed in ticks with a frequency to _PyTime_t, like
42-
// QueryPerformanceCounter() with QueryPerformanceFrequency().
1+
// Internal PyTime_t C API: see Doc/c-api/time.rst for the documentation.
432

443
#ifndef Py_INTERNAL_TIME_H
454
#define Py_INTERNAL_TIME_H
@@ -66,14 +25,7 @@ struct _time_runtime_state {
6625
struct timeval;
6726
#endif
6827

69-
// _PyTime_t: Python timestamp with subsecond precision. It can be used to
70-
// store a duration, and so indirectly a date (related to another date, like
71-
// UNIX epoch).
72-
typedef int64_t _PyTime_t;
73-
// _PyTime_MIN nanoseconds is around -292.3 years
74-
#define _PyTime_MIN INT64_MIN
75-
// _PyTime_MAX nanoseconds is around +292.3 years
76-
#define _PyTime_MAX INT64_MAX
28+
typedef PyTime_t _PyTime_t;
7729
#define _SIZEOF_PYTIME_T 8
7830

7931
typedef enum {
@@ -157,7 +109,7 @@ PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t
157109
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
158110

159111
// Create a timestamp from a number of microseconds.
160-
// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
112+
// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
161113
extern _PyTime_t _PyTime_FromMicrosecondsClamp(_PyTime_t us);
162114

163115
// Create a timestamp from nanoseconds (Python int).
@@ -179,10 +131,6 @@ PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
179131
PyObject *obj,
180132
_PyTime_round_t round);
181133

182-
// Convert a timestamp to a number of seconds as a C double.
183-
// Export for '_socket' shared extension.
184-
PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
185-
186134
// Convert timestamp to a number of milliseconds (10^-3 seconds).
187135
// Export for '_ssl' shared extension.
188136
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
@@ -260,11 +208,11 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
260208
#endif
261209

262210

263-
// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
211+
// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
264212
extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2);
265213

266214
// Compute ticks * mul / div.
267-
// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
215+
// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
268216
// The caller must ensure that ((div - 1) * mul) cannot overflow.
269217
extern _PyTime_t _PyTime_MulDiv(_PyTime_t ticks,
270218
_PyTime_t mul,
@@ -278,36 +226,13 @@ typedef struct {
278226
double resolution;
279227
} _Py_clock_info_t;
280228

281-
// Get the current time from the system clock.
282-
//
283-
// If the internal clock fails, silently ignore the error and return 0.
284-
// On integer overflow, silently ignore the overflow and clamp the clock to
285-
// [_PyTime_MIN; _PyTime_MAX].
286-
//
287-
// Use _PyTime_GetSystemClockWithInfo() to check for failure.
288-
// Export for '_random' shared extension.
289-
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
290-
291229
// Get the current time from the system clock.
292230
// On success, set *t and *info (if not NULL), and return 0.
293231
// On error, raise an exception and return -1.
294232
extern int _PyTime_GetSystemClockWithInfo(
295233
_PyTime_t *t,
296234
_Py_clock_info_t *info);
297235

298-
// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
299-
// The clock is not affected by system clock updates. The reference point of
300-
// the returned value is undefined, so that only the difference between the
301-
// results of consecutive calls is valid.
302-
//
303-
// If the internal clock fails, silently ignore the error and return 0.
304-
// On integer overflow, silently ignore the overflow and clamp the clock to
305-
// [_PyTime_MIN; _PyTime_MAX].
306-
//
307-
// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
308-
// Export for '_random' shared extension.
309-
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
310-
311236
// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
312237
// The clock is not affected by system clock updates. The reference point of
313238
// the returned value is undefined, so that only the difference between the
@@ -332,17 +257,6 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
332257
// Export for '_datetime' shared extension.
333258
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
334259

335-
// Get the performance counter: clock with the highest available resolution to
336-
// measure a short duration.
337-
//
338-
// If the internal clock fails, silently ignore the error and return 0.
339-
// On integer overflow, silently ignore the overflow and clamp the clock to
340-
// [_PyTime_MIN; _PyTime_MAX].
341-
//
342-
// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
343-
// Export for '_lsprof' shared extension.
344-
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
345-
346260
// Get the performance counter: clock with the highest available resolution to
347261
// measure a short duration.
348262
//
@@ -355,12 +269,12 @@ extern int _PyTime_GetPerfCounterWithInfo(
355269

356270

357271
// Create a deadline.
358-
// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
272+
// Pseudo code: PyTime_GetMonotonicClock() + timeout.
359273
// Export for '_ssl' shared extension.
360274
PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout);
361275

362276
// Get remaining time from a deadline.
363-
// Pseudo code: deadline - _PyTime_GetMonotonicClock().
277+
// Pseudo code: deadline - PyTime_GetMonotonicClock().
364278
// Export for '_ssl' shared extension.
365279
PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline);
366280

0 commit comments

Comments
 (0)