gh-137400: Fix thread-safety issues when profiling all threads (gh-137518)

There were a few thread-safety issues when profiling or tracing all
threads via PyEval_SetProfileAllThreads or PyEval_SetTraceAllThreads:

* The loop over thread states could crash if a thread exits concurrently
  (in both the free threading and default build)
* The modification of `c_profilefunc` and `c_tracefunc` wasn't
  thread-safe on the free threading build.
This commit is contained in:
Sam Gross 2025-08-13 14:15:12 -04:00 committed by GitHub
parent 923d68655b
commit a10152f8fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 431 additions and 241 deletions

View file

@ -324,7 +324,6 @@ _Py_COMP_DIAG_POP
&(runtime)->unicode_state.ids.mutex, \
&(runtime)->imports.extensions.mutex, \
&(runtime)->ceval.pending_mainthread.mutex, \
&(runtime)->ceval.sys_trace_profile_mutex, \
&(runtime)->atexit.mutex, \
&(runtime)->audit_hooks.mutex, \
&(runtime)->allocators.mutex, \
@ -565,8 +564,6 @@ init_interpreter(PyInterpreterState *interp,
}
interp->monitoring_tool_versions[t] = 0;
}
interp->sys_profile_initialized = false;
interp->sys_trace_initialized = false;
interp->_code_object_generation = 0;
interp->jit = false;
interp->executor_list_head = NULL;
@ -773,8 +770,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
Py_CLEAR(interp->monitoring_callables[t][e]);
}
}
interp->sys_profile_initialized = false;
interp->sys_trace_initialized = false;
for (int t = 0; t < PY_MONITORING_TOOL_IDS; t++) {
Py_CLEAR(interp->monitoring_tool_names[t]);
}
@ -1689,19 +1684,15 @@ PyThreadState_Clear(PyThreadState *tstate)
"PyThreadState_Clear: warning: thread still has a generator\n");
}
FT_MUTEX_LOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
if (tstate->c_profilefunc != NULL) {
tstate->interp->sys_profiling_threads--;
FT_ATOMIC_ADD_SSIZE(tstate->interp->sys_profiling_threads, -1);
tstate->c_profilefunc = NULL;
}
if (tstate->c_tracefunc != NULL) {
tstate->interp->sys_tracing_threads--;
FT_ATOMIC_ADD_SSIZE(tstate->interp->sys_tracing_threads, -1);
tstate->c_tracefunc = NULL;
}
FT_MUTEX_UNLOCK(&_PyRuntime.ceval.sys_trace_profile_mutex);
Py_CLEAR(tstate->c_profileobj);
Py_CLEAR(tstate->c_traceobj);