Skip to content

Commit d73482e

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_Monotonic(), PyTime_PerfCounter() and PyTime_GetSystemClock() functions. Changes: * Add Include/cpython/pytime.h header. * Add Modules/_testcapi/time.c and Lib/test/test_capi/test_time.py tests on these new functions. * Rename _PyTime_GetMonotonicClock() to PyTime_Monotonic(). * Rename _PyTime_GetPerfCounter() to PyTime_PerfCounter(). * Rename _PyTime_GetSystemClock() to PyTime_Time(). * Rename _PyTime_AsSecondsDouble() to PyTime_AsSecondsDouble(). * Rename _PyTime_MIN to PyTime_MIN. * Rename _PyTime_MAX to PyTime_MAX.
1 parent fb4cb7c commit d73482e

File tree

19 files changed

+419
-151
lines changed

19 files changed

+419
-151
lines changed

Doc/c-api/time.rst

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 with a resolution of one nanosecond.
13+
14+
The :c:type:`PyTime_t` type is signed to support negative timestamps. The
15+
supported range is around [-292.3 years; +292.3 years]. Using the Unix epoch
16+
(January 1st, 1970) as reference, the supported date range is around
17+
[1677-09-21; 2262-04-11].
18+
19+
20+
Types
21+
-----
22+
23+
.. c:type:: PyTime_t
24+
25+
Timestamp type with subsecond precision: 64-bit signed integer.
26+
27+
This type can be used to store a duration. Indirectly, it can be used to
28+
store a date relative to a reference date, such as the UNIX epoch.
29+
30+
31+
Constants
32+
---------
33+
34+
.. c:var:: PyTime_t PyTime_MIN
35+
36+
Minimum value of the :c:type:`PyTime_t` type.
37+
38+
:c:var:`PyTime_MIN` nanoseconds is around -292.3 years.
39+
40+
.. c:var:: PyTime_t PyTime_MAX
41+
42+
Maximum value of the :c:type:`PyTime_t` type.
43+
44+
:c:var:`PyTime_MAX` nanoseconds is around +292.3 years.
45+
46+
47+
Functions
48+
---------
49+
50+
.. c:function:: double PyTime_AsSecondsDouble(PyTime_t t)
51+
52+
Convert a timestamp to a number of seconds as a C :c:expr:`double`.
53+
54+
The function cannot fail.
55+
56+
57+
.. c:function:: PyTime_t PyTime_Monotonic(void)
58+
59+
Get the monotonic clock: clock that cannot go backwards.
60+
61+
The monotonic clock is not affected by system clock updates. The reference
62+
point of the returned value is undefined, so that only the difference
63+
between the results of consecutive calls is valid.
64+
65+
If reading the clock fails, silently ignore the error and return 0.
66+
67+
On integer overflow, silently ignore the overflow and clamp the clock to
68+
the ``[PyTime_MIN; PyTime_MAX]`` range.
69+
70+
See also the :func:`time.monotonic` function.
71+
72+
73+
.. c:function:: PyTime_t PyTime_PerfCounter(void)
74+
75+
Get the performance counter: clock with the highest available resolution to
76+
measure a short duration.
77+
78+
The performance counter does include time elapsed during sleep and is
79+
system-wide. The reference point of the returned value is undefined, so that
80+
only the difference between the results of two calls is valid.
81+
82+
If reading the clock fails, silently ignore the error and return 0.
83+
84+
On integer overflow, silently ignore the overflow and clamp the clock to
85+
the ``[PyTime_MIN; PyTime_MAX]`` range.
86+
87+
See also the :func:`time.perf_counter` function.
88+
89+
90+
.. c:function:: PyTime_t PyTime_Time(void)
91+
92+
Get the system clock.
93+
94+
The system clock can be changed automatically (e.g. by a NTP daemon) or
95+
manually by the system administrator. So it can also go backward. Use
96+
:c:func:`PyTime_Monotonic` to use a monotonic clock.
97+
98+
If reading the clock fails, silently ignore the error and return ``0``.
99+
100+
On integer overflow, silently ignore the overflow and clamp the clock to
101+
the ``[PyTime_MIN; PyTime_MAX]`` range.
102+
103+
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/conf.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,14 @@
119119
('c:type', 'wchar_t'),
120120
('c:type', '__int64'),
121121
('c:type', 'unsigned __int64'),
122+
('c:type', 'double'),
122123
# Standard C structures
123124
('c:struct', 'in6_addr'),
124125
('c:struct', 'in_addr'),
125126
('c:struct', 'stat'),
126127
('c:struct', 'statvfs'),
128+
('c:struct', 'timeval'),
129+
('c:struct', 'timespec'),
127130
# Standard C macros
128131
('c:macro', 'LLONG_MAX'),
129132
('c:macro', 'LLONG_MIN'),
@@ -251,12 +254,12 @@
251254
('py:attr', '__wrapped__'),
252255
]
253256

254-
# gh-106948: Copy standard C types declared in the "c:type" domain to the
255-
# "c:identifier" domain, since "c:function" markup looks for types in the
256-
# "c:identifier" domain. Use list() to not iterate on items which are being
257-
# added
257+
# gh-106948: Copy standard C types declared in the "c:type" domain and C
258+
# structures declared in the "c:struct" domain to the "c:identifier" domain,
259+
# since "c:function" markup looks for types in the "c:identifier" domain. Use
260+
# list() to not iterate on items which are being added
258261
for role, name in list(nitpick_ignore):
259-
if role == 'c:type':
262+
if role in ('c:type', 'c:struct'):
260263
nitpick_ignore.append(('c:identifier', name))
261264
del role, name
262265

Doc/whatsnew/3.13.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,16 @@ New Features
12811281
* Add :c:func:`Py_HashPointer` function to hash a pointer.
12821282
(Contributed by Victor Stinner in :gh:`111545`.)
12831283

1284+
* Add PyTime C API:
1285+
1286+
* :c:type:`PyTime_t` type.
1287+
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
1288+
* :c:func:`PyTime_AsSecondsDouble`, :c:func:`PyTime_Monotonic`,
1289+
:c:func:`PyTime_PerfCounter` and :c:func:`PyTime_Time`
1290+
functions.
1291+
1292+
(Contributed by Victor Stinner in :gh:`110850`.)
1293+
12841294

12851295
Porting to Python 3.13
12861296
----------------------

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_Monotonic(void);
16+
PyAPI_FUNC(PyTime_t) PyTime_PerfCounter(void);
17+
PyAPI_FUNC(PyTime_t) PyTime_Time(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: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
1-
// The _PyTime_t API is written to use timestamp and timeout values stored in
2-
// various formats and to read clocks.
1+
// Internal PyTime_t C API: see Doc/c-api/time.rst for the documentation.
32
//
4-
// The _PyTime_t type is an integer to support directly common arithmetic
5-
// operations like t1 + t2.
3+
// The PyTime_t type is an integer to support directly common arithmetic
4+
// operations such as t1 + t2.
65
//
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].
6+
// Time formats:
117
//
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)
8+
// * Seconds.
9+
// * Seconds as a floating point number (C double).
10+
// * Milliseconds (10^-3 seconds).
11+
// * Microseconds (10^-6 seconds).
12+
// * 100 nanoseconds (10^-7 seconds), used on Windows.
13+
// * Nanoseconds (10^-9 seconds).
14+
// * timeval structure, 1 microsecond (10^-6 seconds).
15+
// * timespec structure, 1 nanosecond (10^-9 seconds).
2216
//
2317
// 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).
18+
// resolution larger than 1 nanosecond is rounded correctly with the requested
19+
// rounding mode. Available rounding modes:
20+
//
21+
// * Round towards minus infinity (-inf). For example, used to read a clock.
22+
// * Round towards infinity (+inf). For example, used for timeout to wait "at
23+
// least" N seconds.
24+
// * Round to nearest with ties going to nearest even integer. For example, used
25+
// to round from a Python float.
26+
// * Round away from zero. For example, used for timeout.
2727
//
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.
28+
// Some functions clamp the result in the range [PyTime_MIN; PyTime_MAX]. The
29+
// caller doesn't have to handle errors and so doesn't need to hold the GIL to
30+
// handle exceptions. For example, _PyTime_Add(t1, t2) computes t1+t2 and
31+
// clamps the result on overflow.
3232
//
3333
// Clocks:
3434
//
3535
// * System clock
3636
// * Monotonic clock
3737
// * Performance counter
3838
//
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().
39+
// Internally, operations like (t * k / q) with integers are implemented in a
40+
// way to reduce the risk of integer overflow. Such operation is used to convert a
41+
// clock value expressed in ticks with a frequency to PyTime_t, like
42+
// QueryPerformanceCounter() with QueryPerformanceFrequency() on Windows.
43+
4344

4445
#ifndef Py_INTERNAL_TIME_H
4546
#define Py_INTERNAL_TIME_H
@@ -56,14 +57,7 @@ extern "C" {
5657
struct timeval;
5758
#endif
5859

59-
// _PyTime_t: Python timestamp with subsecond precision. It can be used to
60-
// store a duration, and so indirectly a date (related to another date, like
61-
// UNIX epoch).
62-
typedef int64_t _PyTime_t;
63-
// _PyTime_MIN nanoseconds is around -292.3 years
64-
#define _PyTime_MIN INT64_MIN
65-
// _PyTime_MAX nanoseconds is around +292.3 years
66-
#define _PyTime_MAX INT64_MAX
60+
typedef PyTime_t _PyTime_t;
6761
#define _SIZEOF_PYTIME_T 8
6862

6963
typedef enum {
@@ -147,7 +141,7 @@ PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t
147141
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
148142

149143
// Create a timestamp from a number of microseconds.
150-
// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
144+
// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
151145
extern _PyTime_t _PyTime_FromMicrosecondsClamp(_PyTime_t us);
152146

153147
// Create a timestamp from nanoseconds (Python int).
@@ -169,10 +163,6 @@ PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
169163
PyObject *obj,
170164
_PyTime_round_t round);
171165

172-
// Convert a timestamp to a number of seconds as a C double.
173-
// Export for '_socket' shared extension.
174-
PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
175-
176166
// Convert timestamp to a number of milliseconds (10^-3 seconds).
177167
// Export for '_ssl' shared extension.
178168
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
@@ -250,7 +240,7 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
250240
#endif
251241

252242

253-
// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
243+
// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
254244
extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2);
255245

256246
// Structure used by time.get_clock_info()
@@ -261,36 +251,13 @@ typedef struct {
261251
double resolution;
262252
} _Py_clock_info_t;
263253

264-
// Get the current time from the system clock.
265-
//
266-
// If the internal clock fails, silently ignore the error and return 0.
267-
// On integer overflow, silently ignore the overflow and clamp the clock to
268-
// [_PyTime_MIN; _PyTime_MAX].
269-
//
270-
// Use _PyTime_GetSystemClockWithInfo() to check for failure.
271-
// Export for '_random' shared extension.
272-
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
273-
274254
// Get the current time from the system clock.
275255
// On success, set *t and *info (if not NULL), and return 0.
276256
// On error, raise an exception and return -1.
277257
extern int _PyTime_GetSystemClockWithInfo(
278258
_PyTime_t *t,
279259
_Py_clock_info_t *info);
280260

281-
// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
282-
// The clock is not affected by system clock updates. The reference point of
283-
// the returned value is undefined, so that only the difference between the
284-
// results of consecutive calls is valid.
285-
//
286-
// If the internal clock fails, silently ignore the error and return 0.
287-
// On integer overflow, silently ignore the overflow and clamp the clock to
288-
// [_PyTime_MIN; _PyTime_MAX].
289-
//
290-
// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
291-
// Export for '_random' shared extension.
292-
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
293-
294261
// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
295262
// The clock is not affected by system clock updates. The reference point of
296263
// the returned value is undefined, so that only the difference between the
@@ -315,17 +282,6 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
315282
// Export for '_datetime' shared extension.
316283
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
317284

318-
// Get the performance counter: clock with the highest available resolution to
319-
// measure a short duration.
320-
//
321-
// If the internal clock fails, silently ignore the error and return 0.
322-
// On integer overflow, silently ignore the overflow and clamp the clock to
323-
// [_PyTime_MIN; _PyTime_MAX].
324-
//
325-
// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
326-
// Export for '_lsprof' shared extension.
327-
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
328-
329285
// Get the performance counter: clock with the highest available resolution to
330286
// measure a short duration.
331287
//
@@ -336,14 +292,24 @@ extern int _PyTime_GetPerfCounterWithInfo(
336292
_PyTime_t *t,
337293
_Py_clock_info_t *info);
338294

295+
// Alias for backward compatibility
296+
#define _PyTime_MIN PyTime_MIN
297+
#define _PyTime_MAX PyTime_MAX
298+
#define _PyTime_AsSecondsDouble PyTime_AsSecondsDouble
299+
#define _PyTime_GetMonotonicClock PyTime_Monotonic
300+
#define _PyTime_GetPerfCounter PyTime_PerfCounter
301+
#define _PyTime_GetSystemClock PyTime_Time
302+
303+
304+
// --- _PyDeadline -----------------------------------------------------------
339305

340306
// Create a deadline.
341-
// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
307+
// Pseudo code: PyTime_Monotonic() + timeout.
342308
// Export for '_ssl' shared extension.
343309
PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout);
344310

345311
// Get remaining time from a deadline.
346-
// Pseudo code: deadline - _PyTime_GetMonotonicClock().
312+
// Pseudo code: deadline - PyTime_Monotonic().
347313
// Export for '_ssl' shared extension.
348314
PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline);
349315

0 commit comments

Comments
 (0)