8000 gh-110850: Add PyTime_t C API (GH-115215) · python/cpython@879f454 · GitHub
[go: up one dir, main page]

Skip to content

Commit 879f454

Browse files
encukouvstinner
andauthored
gh-110850: Add PyTime_t C API (GH-115215)
* 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. Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent c39272e commit 879f454

File tree

19 files changed

+448
-114
lines changed

19 files changed

+448
-114
lines changed

Doc/c-api/time.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
.. highlight:: c
2+
3+
PyTime C API
4+
============
5+
6+
.. versionadded:: 3.13
7+
8+
The clock C API provides access to system clocks.
9+
It is similar to the Python :mod:`time` module.
10+
11+
For C API related to the :mod:`datetime` module, see :ref:`datetimeobjects`.
12+
13+
14+
Types
15+
-----
16+
17+
.. c:type:: PyTime_t
18+
19+
A timestamp or duration in nanoseconds, represented as a signed 64-bit
20+
integer.
21+
22+
The reference point for timestamps depends on the clock used. For example,
23+
:c:func:`PyTime_Time` returns timestamps relative to the UNIX epoch.
24+
25+
The supported range is around [-292.3 years; +292.3 years].
26+
Using the Unix epoch (January 1st, 1970) as reference, the supported date
27+
range is around [1677-09-21; 2262-04-11].
28+
The exact limits are exposed as constants:
29+
30+
.. c:var:: PyTime_t PyTime_MIN
31+
32+
Minimum value of :c:type:`PyTime_t`.
33+
34+
.. c:var:: PyTime_t PyTime_MAX
35+
36+
Maximum value of :c:type:`PyTime_t`.
37+
38+
39+
Clock Functions
40+
---------------
41+
42+
The following functions take a pointer to a :c:expr:`PyTime_t` that they
43+
set to the value of a particular clock.
44+
Details of each clock are given in the documentation of the corresponding
45+
Python function.
46+
47+
The functions return ``0`` on success, or ``-1`` (with an exception set)
48+
on failure.
49+
50+
On integer overflow, they set the :c:data:`PyExc_OverflowError` exception and
51+
set ``*result`` to the value clamped to the ``[PyTime_MIN; PyTime_MAX]``
52+
range.
53+
(On current systems, integer overflows are likely caused by misconfigured
54+
system time.)
55+
56+
As any other C API (unless otherwise specified), the functions must be called
57+
with the :term:`GIL` held.
58+
59+
.. c:function:: int PyTime_Monotonic(PyTime_t *result)
60+
61+
Read the monotonic clock.
62+
See :func:`time.monotonic` for important details on this clock.
63+
64+
.. c:function:: int PyTime_PerfCounter(PyTime_t *result)
65+
66+
Read the performance counter.
67+
See :func:`time.perf_counter` for important details on this clock.
68+
69+
.. c:function:: int PyTime_Time(PyTime_t *result)
70+
71+
Read the “wall clock” time.
72+
See :func:`time.time` for details important on this clock.
73+
74+
75+
Conversion functions
76+
--------------------
77+
78+
.. c:function:: double PyTime_AsSecondsDouble(PyTime_t t)
79+
80+
Convert a timestamp to a number of seconds as a C :c:expr:`double`.
81+
82+
The function cannot fail, but note that :c:expr:`double` has limited
83+
accuracy for large values.

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
@@ -135,11 +135,14 @@
135135
('c:type', 'wchar_t'),
136136
('c:type', '__int64'),
137137
('c:type', 'unsigned __int64'),
138+
('c:type', 'double'),
138139
# Standard C structures
139140
('c:struct', 'in6_addr'),
140141
('c:struct', 'in_addr'),
141142
('c:struct', 'stat'),
142143
('c:struct', 'statvfs'),
144+
('c:struct', 'timeval'),
145+
('c:struct', 'timespec'),
143146
# Standard C macros
144147
('c:macro', 'LLONG_MAX'),
145148
('c:macro', 'LLONG_MIN'),
@@ -269,12 +272,12 @@
269272
('py:meth', 'index'), # list.index, tuple.index, etc.
270273
]
271274

272-
# gh-106948: Copy standard C types declared in the "c:type" domain to the
273-
# "c:identifier" domain, since "c:function" markup looks for types in the
274-
# "c:identifier" domain. Use list() to not iterate on items which are being
275-
# added
275+
# gh-106948: Copy standard C types declared in the "c:type" domain and C
276+
# structures declared in the "c:struct" domain to the "c:identifier" domain,
277+
# since "c:function" markup looks for types in the "c:identifier" domain. Use
278+
# list() to not iterate on items which are being added
276279
for role, name in list(nitpick_ignore):
277-
if role == 'c:type':
280+
if role in ('c:type', 'c:struct'):
278281
nitpick_ignore.append(('c:identifier', name))
279282
del role, name
280283

Doc/whatsnew/3.13.rst

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

1519+
* Add PyTime C API:
1520+
1521+
* :c:type:`PyTime_t` type.
1522+
* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
1523+
* :c:func:`PyTime_AsSecondsDouble`
1524+
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
1525+
:c:func:`PyTime_Time` functions.
1526+
1527+
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
1528+
15191529

15201530
Porting to Python 3.13
15211531
----------------------

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(int) PyTime_Monotonic(PyTime_t *result);
16+
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
17+
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
#endif /* Py_PYTIME_H */
23+
#endif /* Py_LIMITED_API */

Include/internal/pycore_time.h

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
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:
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).
1316
//
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)
17+
// Note that PyTime_t is now specified as int64_t, in nanoseconds.
18+
// (If we need to change this, we'll need new public API with new names.)
19+
// Previously, PyTime_t was configurable (in theory); some comments and code
20+
// might still allude to that.
2221
//
2322
// 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).
23+
// resolution larger than 1 nanosecond is rounded correctly with the requested
24+
// rounding mode. Available rounding modes:
2725
//
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.
26+
// * Round towards minus infinity (-inf). For example, used to read a clock.
27+
// * Round towards infinity (+inf). For example, used for timeout to wait "at
28+
// least" N seconds.
29+
// * Round to nearest with ties going to nearest even integer. For example, used
30+
// to round from a Python float.
31+
// * Round away from zero. For example, used for timeout.
32+
//
33+
// Some functions clamp the result in the range [PyTime_MIN; PyTime_MAX]. The
34+
// caller doesn't have to handle errors and so doesn't need to hold the GIL to
35+
// handle exceptions. For example, _PyTime_Add(t1, t2) computes t1+t2 and
36+
// clamps the result on overflow.
3237
//
3338
// Clocks:
3439
//
3540
// * System clock
3641
// * Monotonic clock
3742
// * Performance counter
3843
//
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().
44+
// Internally, operations like (t * k / q) with integers are implemented in a
45+
// way to reduce the risk of integer overflow. Such operation is used to convert a
46+
// clock value expressed in ticks with a frequency to PyTime_t, like
47+
// QueryPerformanceCounter() with QueryPerformanceFrequency() on Windows.
48+
4349

4450
#ifndef Py_INTERNAL_TIME_H
4551
#define Py_INTERNAL_TIME_H
@@ -56,14 +62,7 @@ extern "C" {
5662
struct timeval;
5763
#endif
5864

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
65+
typedef PyTime_t _PyTime_t;
6766
#define _SIZEOF_PYTIME_T 8
6867

6968
typedef enum {
@@ -147,7 +146,7 @@ PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t
147146
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
148147

149148
// Create a timestamp from a number of microseconds.
150-
// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
149+
// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
151150
extern _PyTime_t _PyTime_FromMicrosecondsClamp(_PyTime_t us);
152151

153152
// Create a timestamp from nanoseconds (Python int).
@@ -169,10 +168,6 @@ PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
169168
PyObject *obj,
170169
_PyTime_round_t round);
171170

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-
176171
// Convert timestamp to a number of milliseconds (10^-3 seconds).
177172
// Export for '_ssl' shared extension.
178173
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
@@ -183,9 +178,6 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
183178
PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
184179
_PyTime_round_t round);
185180

186-
// Convert timestamp to a number of nanoseconds (10^-9 seconds).
187-
extern _PyTime_t _PyTime_AsNanoseconds(_PyTime_t t);
188-
189181
#ifdef MS_WINDOWS
190182
// Convert timestamp to a number of 100 nanoseconds (10^-7 seconds).
191183
extern _PyTime_t _PyTime_As100Nanoseconds(_PyTime_t t,
@@ -250,7 +242,7 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
250242
#endif
251243

252244

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

256248
// Structure used by time.get_clock_info()
@@ -267,7 +259,8 @@ typedef struct {
267259
// On integer overflow, silently ignore the overflow and clamp the clock to
268260
// [_PyTime_MIN; _PyTime_MAX].
269261
//
270-
// Use _PyTime_GetSystemClockWithInfo() to check for failure.
262+
// Use _PyTime_GetSystemClockWithInfo or the public PyTime_Time() to check
263+
// for failure.
271264
// Export for '_random' shared extension.
272265
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
273266

@@ -287,7 +280,8 @@ extern int _PyTime_GetSystemClockWithInfo(
287280
// On integer overflow, silently ignore the overflow and clamp the clock to
288281
// [_PyTime_MIN; _PyTime_MAX].
289282
//
290-
// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
283+
// Use _PyTime_GetMonotonicClockWithInfo or the public PyTime_Monotonic()
284+
// to check for failure.
291285
// Export for '_random' shared extension.
292286
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
293287

@@ -322,10 +316,12 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
322316
// On integer overflow, silently ignore the overflow and clamp the clock to
323317
// [_PyTime_MIN; _PyTime_MAX].
324318
//
325-
// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
319+
// Use _PyTime_GetPerfCounterWithInfo() or the public PyTime_PerfCounter
320+
// to check for failure.
326321
// Export for '_lsprof' shared extension.
327322
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
328323

324+
329325
// Get the performance counter: clock with the highest available resolution to
330326
// measure a short duration.
331327
//
@@ -336,6 +332,13 @@ extern int _PyTime_GetPerfCounterWithInfo(
336332
_PyTime_t *t,
337333
_Py_clock_info_t *info);
338334

335+
// Alias for backward compatibility
336+
#define _PyTime_MIN PyTime_MIN
337+
#define _PyTime_MAX PyTime_MAX
338+
#define _PyTime_AsSecondsDouble PyTime_AsSecondsDouble
339+
340+
341+
// --- _PyDeadline -----------------------------------------------------------
339342

340343
// Create a deadline.
341344
// Pseudo code: _PyTime_GetMonotonicClock() + timeout.

0 commit comments

Comments
 (0)
0