Skip to content

Commit d027787

Browse files
authored
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.
1 parent 45a24f5 commit d027787

File tree

4 files changed

+45
-48
lines changed

4 files changed

+45
-48
lines changed

Include/internal/pycore_runtime.h

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern "C" {
2323
#include "pycore_pymem.h" // struct _pymem_allocators
2424
#include "pycore_pythread.h" // struct _pythread_runtime_state
2525
#include "pycore_signal.h" // struct _signals_runtime_state
26+
#include "pycore_time.h" // struct _PyTime_runtime_state
2627
#include "pycore_tracemalloc.h" // struct _tracemalloc_runtime_state
2728
#include "pycore_typeobject.h" // struct _types_runtime_state
2829
#include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_state
@@ -168,6 +169,7 @@ typedef struct pyruntimestate {
168169
struct _Py_float_runtime_state float_state;
169170
struct _Py_unicode_runtime_state unicode_state;
170171
struct _types_runtime_state types;
172+
struct _Py_time_runtime_state time;
171173

172174
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
173175
// Used in "Python/emscripten_trampoline.c" to choose between type

Include/internal/pycore_time.h

+12
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ extern double _PyTimeFraction_Resolution(
331331
const _PyTimeFraction *frac);
332332

333333

334+
// --- _Py_time_runtime_state ------------------------------------------------
335+
336+
struct _Py_time_runtime_state {
337+
#if defined(MS_WINDOWS) || defined(__APPLE__)
338+
_PyTimeFraction base;
339+
#else
340+
char _unused;
341+
#endif
342+
};
343+
344+
extern PyStatus _PyTime_Init(struct _Py_time_runtime_state *state);
345+
334346
#ifdef __cplusplus
335347
}
336348
#endif

Python/pystate.c

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "pycore_pystate.h"
2121
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
2222
#include "pycore_stackref.h" // Py_STACKREF_DEBUG
23+
#include "pycore_time.h" // _PyTime_Init()
2324
#include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap()
2425
#include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts()
2526

@@ -461,6 +462,11 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
461462
assert(!runtime->_initialized);
462463
}
463464

465+
PyStatus status = _PyTime_Init(&runtime->time);
466+
if (_PyStatus_EXCEPTION(status)) {
467+
return status;
468+
}
469+
464470
if (gilstate_tss_init(runtime) != 0) {
465471
_PyRuntimeState_Fini(runtime);
466472
return _PyStatus_NO_MEMORY();

Python/pytime.c

+25-48
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "Python.h"
2+
#include "pycore_initconfig.h" // _PyStatus_ERR
23
#include "pycore_time.h" // PyTime_t
34
#include "pycore_pystate.h" // _Py_AssertHoldsTstate()
45

@@ -56,14 +57,6 @@
5657
#endif
5758

5859

59-
#ifdef MS_WINDOWS
60-
static _PyTimeFraction py_qpc_base = {0, 0};
61-
62-
// Forward declaration
63-
static int py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc);
64-
#endif
65-
66-
6760
static PyTime_t
6861
_PyTime_GCD(PyTime_t x, PyTime_t y)
6962
{
@@ -923,15 +916,9 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
923916
if (info) {
924917
// GetSystemTimePreciseAsFileTime() is implemented using
925918
// QueryPerformanceCounter() internally.
926-
if (py_qpc_base.denom == 0) {
927-
if (py_win_perf_counter_frequency(&py_qpc_base, raise_exc) < 0) {
928-
return -1;
929-
}
930-
}
931-
932919
info->implementation = "GetSystemTimePreciseAsFileTime()";
933920
info->monotonic = 0;
934-
info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
921+
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
935922
info->adjustable = 1;
936923
}
937924

@@ -1043,8 +1030,8 @@ _PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
10431030

10441031

10451032
#ifdef MS_WINDOWS
1046-
static int
1047-
py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
1033+
static PyStatus
1034+
py_win_perf_counter_frequency(_PyTimeFraction *base)
10481035
{
10491036
LARGE_INTEGER freq;
10501037
// Since Windows XP, the function cannot fail.
@@ -1062,13 +1049,9 @@ py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
10621049
// * 10,000,000 (10 MHz): 100 ns resolution
10631050
// * 3,579,545 Hz (3.6 MHz): 279 ns resolution
10641051
if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
1065-
if (raise_exc) {
1066-
PyErr_SetString(PyExc_RuntimeError,
1067-
"invalid QueryPerformanceFrequency");
1068-
}
1069-
return -1;
1052+
return _PyStatus_ERR("invalid QueryPerformanceFrequency");
10701053
}
1071-
return 0;
1054+
return PyStatus_Ok();
10721055
}
10731056

10741057

@@ -1078,15 +1061,9 @@ py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
10781061
{
10791062
assert(info == NULL || raise_exc);
10801063

1081-
if (py_qpc_base.denom == 0) {
1082-
if (py_win_perf_counter_frequency(&py_qpc_base, raise_exc) < 0) {
1083-
return -1;
1084-
}
1085-
}
1086-
10871064
if (info) {
10881065
info->implementation = "QueryPerformanceCounter()";
1089-
info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
1066+
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
10901067
info->monotonic = 1;
10911068
info->adjustable = 0;
10921069
}
@@ -1102,15 +1079,15 @@ py_get_win_perf_counter(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
11021079
"LONGLONG is larger than PyTime_t");
11031080
ticks = (PyTime_t)ticksll;
11041081

1105-
*tp = _PyTimeFraction_Mul(ticks, &py_qpc_base);
1082+
*tp = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
11061083
return 0;
11071084
}
11081085
#endif // MS_WINDOWS
11091086

11101087

11111088
#ifdef __APPLE__
1112-
static int
1113-
py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
1089+
static PyStatus
1090+
py_mach_timebase_info(_PyTimeFraction *base)
11141091
{
11151092
mach_timebase_info_data_t timebase;
11161093
// According to the Technical Q&A QA1398, mach_timebase_info() cannot
@@ -1132,16 +1109,23 @@ py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
11321109
// * (1000000000, 33333335) on PowerPC: ~30 ns
11331110
// * (1000000000, 25000000) on PowerPC: 40 ns
11341111
if (_PyTimeFraction_Set(base, numer, denom) < 0) {
1135-
if (raise_exc) {
1136-
PyErr_SetString(PyExc_RuntimeError,
1137-
"invalid mach_timebase_info");
1138-
}
1139-
return -1;
1112+
return _PyStatus_ERR("invalid mach_timebase_info");
11401113
}
1141-
return 0;
1114+
return PyStatus_Ok();
11421115
}
11431116
#endif
11441117

1118+
PyStatus
1119+
_PyTime_Init(struct _Py_time_runtime_state *state)
1120+
{
1121+
#ifdef MS_WINDOWS
1122+
return py_win_perf_counter_frequency(&state->base);
1123+
#elif defined(__APPLE__)
1124+
return py_mach_timebase_info(&state->base);
1125+
#else
1126+
return PyStatus_Ok();
1127+
#endif
1128+
}
11451129

11461130
// N.B. If raise_exc=0, this may be called without a thread state.
11471131
static int
@@ -1158,16 +1142,9 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
11581142
return -1;
11591143
}
11601144
#elif defined(__APPLE__)
1161-
static _PyTimeFraction base = {0, 0};
1162-
if (base.denom == 0) {
1163-
if (py_mach_timebase_info(&base, raise_exc) < 0) {
1164-
return -1;
1165-
}
1166-
}
1167-
11681145
if (info) {
11691146
info->implementation = "mach_absolute_time()";
1170-
info->resolution = _PyTimeFraction_Resolution(&base);
1147+
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
11711148
info->monotonic = 1;
11721149
info->adjustable = 0;
11731150
}
@@ -1177,7 +1154,7 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
11771154
assert(uticks <= (uint64_t)PyTime_MAX);
11781155
PyTime_t ticks = (PyTime_t)uticks;
11791156

1180-
PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
1157+
PyTime_t ns = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
11811158
*tp = ns;
11821159

11831160
#elif defined(__hpux)

0 commit comments

Comments
 (0)