]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-130421: Fix data race on timebase initialization (gh-130592)
authorSam Gross <colesbury@gmail.com>
Thu, 27 Feb 2025 13:27:54 +0000 (08:27 -0500)
committerGitHub <noreply@github.com>
Thu, 27 Feb 2025 13:27:54 +0000 (13:27 +0000)
Windows and macOS require precomputing a "timebase" in order to convert
OS timestamps into nanoseconds. Retrieve and compute this value during
runtime initialization to avoid data races when accessing the time.

Include/internal/pycore_runtime.h
Include/internal/pycore_time.h
Python/pystate.c
Python/pytime.c

index cf123791eba9ac6ba437baaa4d46768fc140451b..9324ce9eb89d2ca88d523c68f7bd2e27a6a50411 100644 (file)
@@ -23,6 +23,7 @@ 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 _PyTime_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
@@ -168,6 +169,7 @@ typedef struct pyruntimestate {
     struct _Py_float_runtime_state float_state;
     struct _Py_unicode_runtime_state unicode_state;
     struct _types_runtime_state types;
+    struct _Py_time_runtime_state time;
 
 #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
     // Used in "Python/emscripten_trampoline.c" to choose between type
index 205ac5d3781ddd6c6b70ae4f86c8901a1b064737..833987fd6393445209090c3d080b8c35f0dacaa5 100644 (file)
@@ -331,6 +331,18 @@ extern double _PyTimeFraction_Resolution(
     const _PyTimeFraction *frac);
 
 
+// --- _Py_time_runtime_state ------------------------------------------------
+
+struct _Py_time_runtime_state {
+#if defined(MS_WINDOWS) || defined(__APPLE__)
+    _PyTimeFraction base;
+#else
+    char _unused;
+#endif
+};
+
+extern PyStatus _PyTime_Init(struct _Py_time_runtime_state *state);
+
 #ifdef __cplusplus
 }
 #endif
index 09b83cdeb1f42db7635b3bb6156c08f2be92add7..9ebd9fdea607469ddb05d22b973220d9ca39b155 100644 (file)
@@ -20,6 +20,7 @@
 #include "pycore_pystate.h"
 #include "pycore_runtime_init.h"  // _PyRuntimeState_INIT
 #include "pycore_stackref.h"      // Py_STACKREF_DEBUG
+#include "pycore_time.h"          // _PyTime_Init()
 #include "pycore_obmalloc.h"      // _PyMem_obmalloc_state_on_heap()
 #include "pycore_uniqueid.h"      // _PyObject_FinalizePerThreadRefcounts()
 
@@ -461,6 +462,11 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
         assert(!runtime->_initialized);
     }
 
+    PyStatus status = _PyTime_Init(&runtime->time);
+    if (_PyStatus_EXCEPTION(status)) {
+        return status;
+    }
+
     if (gilstate_tss_init(runtime) != 0) {
         _PyRuntimeState_Fini(runtime);
         return _PyStatus_NO_MEMORY();
index c039fc98ce4bde792e6ded6c494c8e53697509c5..b10d5cf927b7e10f0097a7e8dee88811d260015a 100644 (file)
@@ -1,4 +1,5 @@
 #include "Python.h"
+#include "pycore_initconfig.h"    // _PyStatus_ERR
 #include "pycore_time.h"          // PyTime_t
 #include "pycore_pystate.h"       // _Py_AssertHoldsTstate()
 
 #endif
 
 
-#ifdef MS_WINDOWS
-static _PyTimeFraction py_qpc_base = {0, 0};
-
-// Forward declaration
-static int py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc);
-#endif
-
-
 static PyTime_t
 _PyTime_GCD(PyTime_t x, PyTime_t y)
 {
@@ -923,15 +916,9 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
     if (info) {
         // GetSystemTimePreciseAsFileTime() is implemented using
         // QueryPerformanceCounter() internally.
-        if (py_qpc_base.denom == 0) {
-            if (py_win_perf_counter_frequency(&py_qpc_base, raise_exc) < 0) {
-                return -1;
-            }
-        }
-
         info->implementation = "GetSystemTimePreciseAsFileTime()";
         info->monotonic = 0;
-        info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
+        info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
         info->adjustable = 1;
     }
 
@@ -1043,8 +1030,8 @@ _PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
 
 
 #ifdef MS_WINDOWS
-static int
-py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
+static PyStatus
+py_win_perf_counter_frequency(_PyTimeFraction *base)
 {
     LARGE_INTEGER freq;
     // Since Windows XP, the function cannot fail.
@@ -1062,13 +1049,9 @@ py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
     // * 10,000,000 (10 MHz): 100 ns resolution
     // * 3,579,545 Hz (3.6 MHz): 279 ns resolution
     if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
-        if (raise_exc) {
-            PyErr_SetString(PyExc_RuntimeError,
-                            "invalid QueryPerformanceFrequency");
-        }
-        return -1;
+        return _PyStatus_ERR("invalid QueryPerformanceFrequency");
     }
-    return 0;
+    return PyStatus_Ok();
 }
 
 
@@ -1078,15 +1061,9 @@ py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
 {
     assert(info == NULL || raise_exc);
 
-    if (py_qpc_base.denom == 0) {
-        if (py_win_perf_counter_frequency(&py_qpc_base, raise_exc) < 0) {
-            return -1;
-        }
-    }
-
     if (info) {
         info->implementation = "QueryPerformanceCounter()";
-        info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
+        info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
         info->monotonic = 1;
         info->adjustable = 0;
     }
@@ -1102,15 +1079,15 @@ py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
                   "LONGLONG is larger than PyTime_t");
     ticks = (PyTime_t)ticksll;
 
-    *tp = _PyTimeFraction_Mul(ticks, &py_qpc_base);
+    *tp = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
     return 0;
 }
 #endif  // MS_WINDOWS
 
 
 #ifdef __APPLE__
-static int
-py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
+static PyStatus
+py_mach_timebase_info(_PyTimeFraction *base)
 {
     mach_timebase_info_data_t timebase;
     // According to the Technical Q&A QA1398, mach_timebase_info() cannot
@@ -1132,16 +1109,23 @@ py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
     // * (1000000000, 33333335) on PowerPC: ~30 ns
     // * (1000000000, 25000000) on PowerPC: 40 ns
     if (_PyTimeFraction_Set(base, numer, denom) < 0) {
-        if (raise_exc) {
-            PyErr_SetString(PyExc_RuntimeError,
-                            "invalid mach_timebase_info");
-        }
-        return -1;
+        return _PyStatus_ERR("invalid mach_timebase_info");
     }
-    return 0;
+    return PyStatus_Ok();
 }
 #endif
 
+PyStatus
+_PyTime_Init(struct _Py_time_runtime_state *state)
+{
+#ifdef MS_WINDOWS
+    return py_win_perf_counter_frequency(&state->base);
+#elif defined(__APPLE__)
+    return py_mach_timebase_info(&state->base);
+#else
+    return PyStatus_Ok();
+#endif
+}
 
 // N.B. If raise_exc=0, this may be called without a thread state.
 static int
@@ -1158,16 +1142,9 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
         return -1;
     }
 #elif defined(__APPLE__)
-    static _PyTimeFraction base = {0, 0};
-    if (base.denom == 0) {
-        if (py_mach_timebase_info(&base, raise_exc) < 0) {
-            return -1;
-        }
-    }
-
     if (info) {
         info->implementation = "mach_absolute_time()";
-        info->resolution = _PyTimeFraction_Resolution(&base);
+        info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
         info->monotonic = 1;
         info->adjustable = 0;
     }
@@ -1177,7 +1154,7 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
     assert(uticks <= (uint64_t)PyTime_MAX);
     PyTime_t ticks = (PyTime_t)uticks;
 
-    PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
+    PyTime_t ns = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
     *tp = ns;
 
 #elif defined(__hpux)