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.
This commit is contained in:
parent
45a24f54af
commit
d027787c8d
@ -23,6 +23,7 @@ extern "C" {
|
|||||||
#include "pycore_pymem.h" // struct _pymem_allocators
|
#include "pycore_pymem.h" // struct _pymem_allocators
|
||||||
#include "pycore_pythread.h" // struct _pythread_runtime_state
|
#include "pycore_pythread.h" // struct _pythread_runtime_state
|
||||||
#include "pycore_signal.h" // struct _signals_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_tracemalloc.h" // struct _tracemalloc_runtime_state
|
||||||
#include "pycore_typeobject.h" // struct _types_runtime_state
|
#include "pycore_typeobject.h" // struct _types_runtime_state
|
||||||
#include "pycore_unicodeobject.h" // struct _Py_unicode_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_float_runtime_state float_state;
|
||||||
struct _Py_unicode_runtime_state unicode_state;
|
struct _Py_unicode_runtime_state unicode_state;
|
||||||
struct _types_runtime_state types;
|
struct _types_runtime_state types;
|
||||||
|
struct _Py_time_runtime_state time;
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
|
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
|
||||||
// Used in "Python/emscripten_trampoline.c" to choose between type
|
// Used in "Python/emscripten_trampoline.c" to choose between type
|
||||||
|
@ -331,6 +331,18 @@ extern double _PyTimeFraction_Resolution(
|
|||||||
const _PyTimeFraction *frac);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "pycore_pystate.h"
|
#include "pycore_pystate.h"
|
||||||
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
|
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
|
||||||
#include "pycore_stackref.h" // Py_STACKREF_DEBUG
|
#include "pycore_stackref.h" // Py_STACKREF_DEBUG
|
||||||
|
#include "pycore_time.h" // _PyTime_Init()
|
||||||
#include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap()
|
#include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap()
|
||||||
#include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts()
|
#include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts()
|
||||||
|
|
||||||
@ -461,6 +462,11 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
|||||||
assert(!runtime->_initialized);
|
assert(!runtime->_initialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyStatus status = _PyTime_Init(&runtime->time);
|
||||||
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
if (gilstate_tss_init(runtime) != 0) {
|
if (gilstate_tss_init(runtime) != 0) {
|
||||||
_PyRuntimeState_Fini(runtime);
|
_PyRuntimeState_Fini(runtime);
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "pycore_initconfig.h" // _PyStatus_ERR
|
||||||
#include "pycore_time.h" // PyTime_t
|
#include "pycore_time.h" // PyTime_t
|
||||||
#include "pycore_pystate.h" // _Py_AssertHoldsTstate()
|
#include "pycore_pystate.h" // _Py_AssertHoldsTstate()
|
||||||
|
|
||||||
@ -56,14 +57,6 @@
|
|||||||
#endif
|
#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
|
static PyTime_t
|
||||||
_PyTime_GCD(PyTime_t x, PyTime_t y)
|
_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) {
|
if (info) {
|
||||||
// GetSystemTimePreciseAsFileTime() is implemented using
|
// GetSystemTimePreciseAsFileTime() is implemented using
|
||||||
// QueryPerformanceCounter() internally.
|
// 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->implementation = "GetSystemTimePreciseAsFileTime()";
|
||||||
info->monotonic = 0;
|
info->monotonic = 0;
|
||||||
info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
|
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
|
||||||
info->adjustable = 1;
|
info->adjustable = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,8 +1030,8 @@ _PyTime_TimeWithInfo(PyTime_t *t, _Py_clock_info_t *info)
|
|||||||
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
static int
|
static PyStatus
|
||||||
py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc)
|
py_win_perf_counter_frequency(_PyTimeFraction *base)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER freq;
|
LARGE_INTEGER freq;
|
||||||
// Since Windows XP, the function cannot fail.
|
// 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
|
// * 10,000,000 (10 MHz): 100 ns resolution
|
||||||
// * 3,579,545 Hz (3.6 MHz): 279 ns resolution
|
// * 3,579,545 Hz (3.6 MHz): 279 ns resolution
|
||||||
if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
|
if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) {
|
||||||
if (raise_exc) {
|
return _PyStatus_ERR("invalid QueryPerformanceFrequency");
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
|
||||||
"invalid QueryPerformanceFrequency");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
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);
|
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) {
|
if (info) {
|
||||||
info->implementation = "QueryPerformanceCounter()";
|
info->implementation = "QueryPerformanceCounter()";
|
||||||
info->resolution = _PyTimeFraction_Resolution(&py_qpc_base);
|
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
|
||||||
info->monotonic = 1;
|
info->monotonic = 1;
|
||||||
info->adjustable = 0;
|
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");
|
"LONGLONG is larger than PyTime_t");
|
||||||
ticks = (PyTime_t)ticksll;
|
ticks = (PyTime_t)ticksll;
|
||||||
|
|
||||||
*tp = _PyTimeFraction_Mul(ticks, &py_qpc_base);
|
*tp = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif // MS_WINDOWS
|
#endif // MS_WINDOWS
|
||||||
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
static int
|
static PyStatus
|
||||||
py_mach_timebase_info(_PyTimeFraction *base, int raise_exc)
|
py_mach_timebase_info(_PyTimeFraction *base)
|
||||||
{
|
{
|
||||||
mach_timebase_info_data_t timebase;
|
mach_timebase_info_data_t timebase;
|
||||||
// According to the Technical Q&A QA1398, mach_timebase_info() cannot
|
// 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, 33333335) on PowerPC: ~30 ns
|
||||||
// * (1000000000, 25000000) on PowerPC: 40 ns
|
// * (1000000000, 25000000) on PowerPC: 40 ns
|
||||||
if (_PyTimeFraction_Set(base, numer, denom) < 0) {
|
if (_PyTimeFraction_Set(base, numer, denom) < 0) {
|
||||||
if (raise_exc) {
|
return _PyStatus_ERR("invalid mach_timebase_info");
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
|
||||||
"invalid mach_timebase_info");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return PyStatus_Ok();
|
||||||
}
|
}
|
||||||
#endif
|
#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.
|
// N.B. If raise_exc=0, this may be called without a thread state.
|
||||||
static int
|
static int
|
||||||
@ -1158,16 +1142,9 @@ py_get_monotonic_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#elif defined(__APPLE__)
|
#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) {
|
if (info) {
|
||||||
info->implementation = "mach_absolute_time()";
|
info->implementation = "mach_absolute_time()";
|
||||||
info->resolution = _PyTimeFraction_Resolution(&base);
|
info->resolution = _PyTimeFraction_Resolution(&_PyRuntime.time.base);
|
||||||
info->monotonic = 1;
|
info->monotonic = 1;
|
||||||
info->adjustable = 0;
|
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);
|
assert(uticks <= (uint64_t)PyTime_MAX);
|
||||||
PyTime_t ticks = (PyTime_t)uticks;
|
PyTime_t ticks = (PyTime_t)uticks;
|
||||||
|
|
||||||
PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
|
PyTime_t ns = _PyTimeFraction_Mul(ticks, &_PyRuntime.time.base);
|
||||||
*tp = ns;
|
*tp = ns;
|
||||||
|
|
||||||
#elif defined(__hpux)
|
#elif defined(__hpux)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user