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

Skip to content
8000

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
  • Include
  • Lib/test
  • Misc/NEWS.d/next/C API
  • Modules
  • PCbuild
  • Python
  • 19 files changed

    +448
    -114
    lines changed

    Doc/c-api/time.rst

    Lines changed: 83 additions & 0 deletions
      8000
    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