aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/pytime.c
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2025-02-27 08:27:54 -0500
committerGitHub <noreply@github.com>2025-02-27 13:27:54 +0000
commitd027787c8d89f59a9f0b1d7cc6972f5e16ffc740 (patch)
tree1128b31a519ee0a1499fd6085881d5d0a107befd /Python/pytime.c
parent45a24f54af4a65c14cc15fc13d3258726e2fe73b (diff)
downloadcpython-d027787c8d89f59a9f0b1d7cc6972f5e16ffc740.tar.gz
cpython-d027787c8d89f59a9f0b1d7cc6972f5e16ffc740.zip
gh-130421: Fix data race on timebase initialization (gh-130592)
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.
Diffstat (limited to 'Python/pytime.c')
-rw-r--r--Python/pytime.c73
1 files changed, 25 insertions, 48 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index c039fc98ce4..b10d5cf927b 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -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()
@@ -56,14 +57,6 @@
#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)