]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112567: Add _Py_GetTicksPerSecond() function (#112587)
authorVictor Stinner <vstinner@python.org>
Fri, 1 Dec 2023 16:05:56 +0000 (17:05 +0100)
committerGitHub <noreply@github.com>
Fri, 1 Dec 2023 16:05:56 +0000 (17:05 +0100)
* Move _PyRuntimeState.time to _posixstate.ticks_per_second and
  time_module_state.ticks_per_second.
* Add time_module_state.clocks_per_second.
* Rename _PyTime_GetClockWithInfo() to py_clock().
* Rename _PyTime_GetProcessTimeWithInfo() to py_process_time().
* Add process_time_times() helper function, called by
  py_process_time().
* os.times() is now always built: no longer rely on HAVE_TIMES.

Include/internal/pycore_fileutils.h
Include/internal/pycore_pylifecycle.h
Include/internal/pycore_runtime.h
Include/internal/pycore_time.h
Modules/clinic/posixmodule.c.h
Modules/posixmodule.c
Modules/timemodule.c
Python/fileutils.c
Python/pylifecycle.c

index 2f89da2c6ecd916a0f39e266cd1f72b549ef0d26..5c55282fa39e6f6062c056af220057847416ab5b 100644 (file)
@@ -320,6 +320,10 @@ PyAPI_FUNC(char*) _Py_UniversalNewlineFgetsWithSize(char *, int, FILE*, PyObject
 
 extern int _PyFile_Flush(PyObject *);
 
+#ifndef MS_WINDOWS
+extern int _Py_GetTicksPerSecond(long *ticks_per_second);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index 61e0150e89009ce403f48c4115c912762d189ea3..daf7cb77dcc63ae5f27a6d0b11a7f43074f959c5 100644 (file)
@@ -40,7 +40,6 @@ extern void _PySys_FiniTypes(PyInterpreterState *interp);
 extern int _PyBuiltins_AddExceptions(PyObject * bltinmod);
 extern PyStatus _Py_HashRandomization_Init(const PyConfig *);
 
-extern PyStatus _PyTime_Init(void);
 extern PyStatus _PyGC_Init(PyInterpreterState *interp);
 extern PyStatus _PyAtExit_Init(PyInterpreterState *interp);
 extern int _Py_Deepfreeze_Init(void);
index e6efe8b646e86f85d10f8db017f16b04ca8bb95b..36743723f8afd880433ece66c811c754db638fde 100644 (file)
@@ -21,7 +21,6 @@ extern "C" {
 #include "pycore_pymem.h"           // struct _pymem_allocators
 #include "pycore_pythread.h"        // struct _pythread_runtime_state
 #include "pycore_signal.h"          // struct _signals_runtime_state
-#include "pycore_time.h"            // struct _time_runtime_state
 #include "pycore_tracemalloc.h"     // struct _tracemalloc_runtime_state
 #include "pycore_typeobject.h"      // struct _types_runtime_state
 #include "pycore_unicodeobject.h"   // struct _Py_unicode_runtime_state
@@ -205,7 +204,6 @@ typedef struct pyruntimestate {
     struct _pymem_allocators allocators;
     struct _obmalloc_global_state obmalloc;
     struct pyhash_runtime_state pyhash_state;
-    struct _time_runtime_state time;
     struct _pythread_runtime_state threads;
     struct _signals_runtime_state signals;
 
index 46713f91d190ffc91fade2f9579e1e13f19cbc67..7ea3485107572e12f635b62ca99d42ba583f7685 100644 (file)
@@ -52,16 +52,6 @@ extern "C" {
 #endif
 
 
-struct _time_runtime_state {
-#ifdef HAVE_TIMES
-    int ticks_per_second_initialized;
-    long ticks_per_second;
-#else
-    int _not_used;
-#endif
-};
-
-
 #ifdef __clang__
 struct timeval;
 #endif
index 9c54935bafa617a6077db536b38a74ecb67268cc..a6c76370f241be1eb5b6b8fadd2159daa1280aa7 100644 (file)
@@ -5997,8 +5997,6 @@ exit:
 
 #endif /* defined(HAVE_SYMLINK) */
 
-#if defined(HAVE_TIMES)
-
 PyDoc_STRVAR(os_times__doc__,
 "times($module, /)\n"
 "--\n"
@@ -6021,8 +6019,6 @@ os_times(PyObject *module, PyObject *Py_UNUSED(ignored))
     return os_times_impl(module);
 }
 
-#endif /* defined(HAVE_TIMES) */
-
 #if defined(HAVE_TIMERFD_CREATE)
 
 PyDoc_STRVAR(os_timerfd_create__doc__,
@@ -12116,10 +12112,6 @@ exit:
     #define OS_SYMLINK_METHODDEF
 #endif /* !defined(OS_SYMLINK_METHODDEF) */
 
-#ifndef OS_TIMES_METHODDEF
-    #define OS_TIMES_METHODDEF
-#endif /* !defined(OS_TIMES_METHODDEF) */
-
 #ifndef OS_TIMERFD_CREATE_METHODDEF
     #define OS_TIMERFD_CREATE_METHODDEF
 #endif /* !defined(OS_TIMERFD_CREATE_METHODDEF) */
@@ -12403,4 +12395,4 @@ exit:
 #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
     #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
 #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
-/*[clinic end generated code: output=0f216bf44ea358f9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=2900675ac5219924 input=a9049054013a1b77]*/
index d99b5335b6989aec32c11e7eac1b6c4d4e500c38..70d107a297f315d8df7194343c223a34b9b8ec0d 100644 (file)
@@ -1030,6 +1030,10 @@ typedef struct {
     PyObject *struct_rusage;
 #endif
     PyObject *st_mode;
+#ifndef MS_WINDOWS
+    // times() clock frequency in hertz; used by os.times()
+    long ticks_per_second;
+#endif
 } _posixstate;
 
 
@@ -9986,8 +9990,6 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
 #endif /* HAVE_SYMLINK */
 
 
-
-
 static PyStructSequence_Field times_result_fields[] = {
     {"user",    "user time"},
     {"system",   "system time"},
@@ -10013,12 +10015,6 @@ static PyStructSequence_Desc times_result_desc = {
     5
 };
 
-#ifdef MS_WINDOWS
-#define HAVE_TIMES  /* mandatory, for the method table */
-#endif
-
-#ifdef HAVE_TIMES
-
 static PyObject *
 build_times_result(PyObject *module, double user, double system,
     double children_user, double children_system,
@@ -10064,8 +10060,8 @@ All fields are floating point numbers.
 static PyObject *
 os_times_impl(PyObject *module)
 /*[clinic end generated code: output=35f640503557d32a input=2bf9df3d6ab2e48b]*/
-#ifdef MS_WINDOWS
 {
+#ifdef MS_WINDOWS
     FILETIME create, exit, kernel, user;
     HANDLE hProc;
     hProc = GetCurrentProcess();
@@ -10083,28 +10079,26 @@ os_times_impl(PyObject *module)
         (double)0,
         (double)0,
         (double)0);
-}
 #else /* MS_WINDOWS */
-{
-    struct tms t;
-    clock_t c;
+    _posixstate *state = get_posix_state(module);
+    long ticks_per_second = state->ticks_per_second;
+
+    struct tms process;
+    clock_t elapsed;
     errno = 0;
-    c = times(&t);
-    if (c == (clock_t) -1) {
+    elapsed = times(&process);
+    if (elapsed == (clock_t) -1) {
         return posix_error();
     }
-    assert(_PyRuntime.time.ticks_per_second_initialized);
-#define ticks_per_second _PyRuntime.time.ticks_per_second
+
     return build_times_result(module,
-                         (double)t.tms_utime / ticks_per_second,
-                         (double)t.tms_stime / ticks_per_second,
-                         (double)t.tms_cutime / ticks_per_second,
-                         (double)t.tms_cstime / ticks_per_second,
-                         (double)c / ticks_per_second);
-#undef ticks_per_second
-}
+        (double)process.tms_utime / ticks_per_second,
+        (double)process.tms_stime / ticks_per_second,
+        (double)process.tms_cutime / ticks_per_second,
+        (double)process.tms_cstime / ticks_per_second,
+        (double)elapsed / ticks_per_second);
 #endif /* MS_WINDOWS */
-#endif /* HAVE_TIMES */
+}
 
 
 #if defined(HAVE_TIMERFD_CREATE)
@@ -17279,6 +17273,15 @@ posixmodule_exec(PyObject *m)
         Py_DECREF(unicode);
     }
 
+#ifndef MS_WINDOWS
+    if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "cannot read ticks_per_second");
+        return -1;
+    }
+    assert(state->ticks_per_second >= 1);
+#endif
+
     return PyModule_Add(m, "_have_functions", list);
 }
 
index bc3901e0d7a6212f12fd96e57b35221375a95dbf..aa0cdc5f026e7c7c0177112bd349699c6d5fa448 100644 (file)
@@ -73,51 +73,20 @@ module time
 static int
 check_ticks_per_second(long tps, const char *context)
 {
-    /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, ticks_per_second)
+    /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, tps)
        cannot overflow. */
     if (tps >= 0 && (_PyTime_t)tps > _PyTime_MAX / SEC_TO_NS) {
         PyErr_Format(PyExc_OverflowError, "%s is too large", context);
         return -1;
     }
+    if (tps < 1) {
+        PyErr_Format(PyExc_RuntimeError, "invalid %s", context);
+        return -1;
+    }
     return 0;
 }
 #endif  /* HAVE_TIMES || HAVE_CLOCK */
 
-#ifdef HAVE_TIMES
-
-# define ticks_per_second _PyRuntime.time.ticks_per_second
-
-static void
-ensure_ticks_per_second(void)
-{
-    if (_PyRuntime.time.ticks_per_second_initialized) {
-        return;
-    }
-    _PyRuntime.time.ticks_per_second_initialized = 1;
-# if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
-    ticks_per_second = sysconf(_SC_CLK_TCK);
-    if (ticks_per_second < 1) {
-        ticks_per_second = -1;
-    }
-# elif defined(HZ)
-    ticks_per_second = HZ;
-# else
-    ticks_per_second = 60;  /* magic fallback value; may be bogus */
-# endif
-}
-
-#endif  /* HAVE_TIMES */
-
-
-PyStatus
-_PyTime_Init(void)
-{
-#ifdef HAVE_TIMES
-    ensure_ticks_per_second();
-#endif
-    return PyStatus_Ok();
-}
-
 
 /* Forward declarations */
 static int pysleep(_PyTime_t timeout);
@@ -125,6 +94,14 @@ static int pysleep(_PyTime_t timeout);
 
 typedef struct {
     PyTypeObject *struct_time_type;
+#ifdef HAVE_TIMES
+    // times() clock frequency in hertz
+    long ticks_per_second;
+#endif
+#ifdef HAVE_CLOCK
+    // clock() frequency in hertz
+    long clocks_per_second;
+#endif
 } time_module_state;
 
 static inline time_module_state*
@@ -184,7 +161,7 @@ PyDoc_STRVAR(time_ns_doc,
 \n\
 Return the current time in nanoseconds since the Epoch.");
 
-#if defined(HAVE_CLOCK)
+#ifdef HAVE_CLOCK
 
 #ifndef CLOCKS_PER_SEC
 #  ifdef CLK_TCK
@@ -195,15 +172,12 @@ Return the current time in nanoseconds since the Epoch.");
 #endif
 
 static int
-_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
+py_clock(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info)
 {
-    if (check_ticks_per_second(CLOCKS_PER_SEC, "CLOCKS_PER_SEC") < 0) {
-        return -1;
-    }
-
+    long clocks_per_second = state->clocks_per_second;
     if (info) {
         info->implementation = "clock()";
-        info->resolution = 1.0 / (double)CLOCKS_PER_SEC;
+        info->resolution = 1.0 / (double)clocks_per_second;
         info->monotonic = 1;
         info->adjustable = 0;
     }
@@ -215,7 +189,7 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
                         "or its value cannot be represented");
         return -1;
     }
-    _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC);
+    _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, clocks_per_second);
     *tp = _PyTime_FromNanoseconds(ns);
     return 0;
 }
@@ -1277,8 +1251,38 @@ PyDoc_STRVAR(perf_counter_ns_doc,
 \n\
 Performance counter for benchmarking as nanoseconds.");
 
+
+#ifdef HAVE_TIMES
 static int
-_PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
+process_time_times(time_module_state *state, _PyTime_t *tp,
+                   _Py_clock_info_t *info)
+{
+    long ticks_per_second = state->ticks_per_second;
+
+    struct tms process;
+    if (times(&process) == (clock_t)-1) {
+        return 0;
+    }
+
+    if (info) {
+        info->implementation = "times()";
+        info->monotonic = 1;
+        info->adjustable = 0;
+        info->resolution = 1.0 / (double)ticks_per_second;
+    }
+
+    _PyTime_t ns;
+    ns = _PyTime_MulDiv(process.tms_utime, SEC_TO_NS, ticks_per_second);
+    ns += _PyTime_MulDiv(process.tms_stime, SEC_TO_NS, ticks_per_second);
+    *tp = _PyTime_FromNanoseconds(ns);
+    return 1;
+}
+#endif
+
+
+static int
+py_process_time(time_module_state *state, _PyTime_t *tp,
+                _Py_clock_info_t *info)
 {
 #if defined(MS_WINDOWS)
     HANDLE process;
@@ -1381,41 +1385,28 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
 
     /* times() */
 #ifdef HAVE_TIMES
-    struct tms t;
-
-    if (times(&t) != (clock_t)-1) {
-        assert(_PyRuntime.time.ticks_per_second_initialized);
-        if (check_ticks_per_second(ticks_per_second, "_SC_CLK_TCK") < 0) {
-            return -1;
-        }
-        if (ticks_per_second != -1) {
-            if (info) {
-                info->implementation = "times()";
-                info->monotonic = 1;
-                info->adjustable = 0;
-                info->resolution = 1.0 / (double)ticks_per_second;
-            }
-
-            _PyTime_t ns;
-            ns = _PyTime_MulDiv(t.tms_utime, SEC_TO_NS, ticks_per_second);
-            ns += _PyTime_MulDiv(t.tms_stime, SEC_TO_NS, ticks_per_second);
-            *tp = _PyTime_FromNanoseconds(ns);
-            return 0;
-        }
+    int res = process_time_times(state, tp, info);
+    if (res < 0) {
+        return -1;
     }
+    if (res == 1) {
+        return 0;
+    }
+    // times() failed, ignore failure
 #endif
 
     /* clock */
     /* Currently, Python 3 requires clock() to build: see issue #22624 */
-    return _PyTime_GetClockWithInfo(tp, info);
+    return py_clock(state, tp, info);
 #endif
 }
 
 static PyObject *
-time_process_time(PyObject *self, PyObject *unused)
+time_process_time(PyObject *module, PyObject *unused)
 {
+    time_module_state *state = get_time_state(module);
     _PyTime_t t;
-    if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) {
+    if (py_process_time(state, &t, NULL) < 0) {
         return NULL;
     }
     return _PyFloat_FromPyTime(t);
@@ -1427,10 +1418,11 @@ PyDoc_STRVAR(process_time_doc,
 Process time for profiling: sum of the kernel and user-space CPU time.");
 
 static PyObject *
-time_process_time_ns(PyObject *self, PyObject *unused)
+time_process_time_ns(PyObject *module, PyObject *unused)
 {
+    time_module_state *state = get_time_state(module);
     _PyTime_t t;
-    if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) {
+    if (py_process_time(state, &t, NULL) < 0) {
         return NULL;
     }
     return _PyTime_AsNanosecondsObject(t);
@@ -1617,7 +1609,7 @@ sum of the kernel and user-space CPU time.");
 
 
 static PyObject *
-time_get_clock_info(PyObject *self, PyObject *args)
+time_get_clock_info(PyObject *module, PyObject *args)
 {
     char *name;
     _Py_clock_info_t info;
@@ -1656,7 +1648,8 @@ time_get_clock_info(PyObject *self, PyObject *args)
         }
     }
     else if (strcmp(name, "process_time") == 0) {
-        if (_PyTime_GetProcessTimeWithInfo(&t, &info) < 0) {
+        time_module_state *state = get_time_state(module);
+        if (py_process_time(state, &t, &info) < 0) {
             return NULL;
         }
     }
@@ -2116,6 +2109,25 @@ time_exec(PyObject *module)
     }
 #endif
 
+#ifdef HAVE_TIMES
+    if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "cannot read ticks_per_second");
+        return -1;
+    }
+
+    if (check_ticks_per_second(state->ticks_per_second, "_SC_CLK_TCK") < 0) {
+        return -1;
+    }
+#endif
+
+#ifdef HAVE_CLOCK
+    state->clocks_per_second = CLOCKS_PER_SEC;
+    if (check_ticks_per_second(state->clocks_per_second, "CLOCKS_PER_SEC") < 0) {
+        return -1;
+    }
+#endif
+
     return 0;
 }
 
index 649b188b5167d033fba46999abb6b1ed44813ec1..9d12bc89c95436a4ef8873109cb7357fa4c259f8 100644 (file)
@@ -2943,3 +2943,27 @@ _Py_closerange(int first, int last)
 #endif /* USE_FDWALK */
     _Py_END_SUPPRESS_IPH
 }
+
+
+#ifndef MS_WINDOWS
+// Ticks per second used by clock() and times() functions.
+// See os.times() and time.process_time() implementations.
+int
+_Py_GetTicksPerSecond(long *ticks_per_second)
+{
+#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
+    long value = sysconf(_SC_CLK_TCK);
+    if (value < 1) {
+        return -1;
+    }
+    *ticks_per_second = value;
+#elif defined(HZ)
+    assert(HZ >= 1);
+    *ticks_per_second = HZ;
+#else
+    // Magic fallback value; may be bogus
+    *ticks_per_second = 60;
+#endif
+    return 0;
+}
+#endif
index aff67d7a835e89889cdd3f9560f65643ed30b0a8..95a72eb47048f253a9d0b6e52bb2cf65077c755d 100644 (file)
@@ -528,11 +528,6 @@ pycore_init_runtime(_PyRuntimeState *runtime,
         return status;
     }
 
-    status = _PyTime_Init();
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
-    }
-
     status = _PyImport_Init();
     if (_PyStatus_EXCEPTION(status)) {
         return status;