https://github.com/python/cpython/commit/e236568c0ea301591ca1517d39ce337e6ee0a48f commit: e236568c0ea301591ca1517d39ce337e6ee0a48f branch: 3.13 author: Sam Gross <colesb...@gmail.com> committer: colesbury <colesb...@gmail.com> date: 2025-08-11T16:07:21Z summary:
[3.13] gh-137400: Fix a crash when disabling profiling across all threads (gh-137471) (gh-137649) The `PyEval_SetProfileAllThreads` function and other related functions had a race condition on `tstate->c_profilefunc` that could lead to a crash when disable profiling or tracing on all threads while another thread is starting to profile or trace a a call. There are still potential crashes when threads exit concurrently with profiling or tracing be enabled/disabled across all threads. (cherry picked from commit 362692852f13cdd1d33cc7ed35c0cbac7af1a785) files: A Misc/NEWS.d/next/Core and Builtins/2025-08-06-15-39-54.gh-issue-137400.xIw0zs.rst M Lib/test/test_free_threading/test_monitoring.py M Python/legacy_tracing.c diff --git a/Lib/test/test_free_threading/test_monitoring.py b/Lib/test/test_free_threading/test_monitoring.py index 8fec01715531cb..51860a7c94539f 100644 --- a/Lib/test/test_free_threading/test_monitoring.py +++ b/Lib/test/test_free_threading/test_monitoring.py @@ -2,6 +2,7 @@ environment to verify things are thread-safe in a free-threaded build""" import sys +import threading import time import unittest import weakref @@ -192,6 +193,40 @@ def during_threads(self): self.set = not self.set +@threading_helper.requires_working_threading() +class SetProfileAllMultiThreaded(TestCase): + def test_profile_all_threads(self): + done = threading.Event() + + def func(): + pass + + def bg_thread(): + while not done.is_set(): + func() + func() + func() + func() + func() + + def my_profile(frame, event, arg): + return None + + bg_threads = [] + for i in range(10): + t = threading.Thread(target=bg_thread) + t.start() + bg_threads.append(t) + + for i in range(100): + threading.setprofile_all_threads(my_profile) + threading.setprofile_all_threads(None) + + done.set() + for t in bg_threads: + t.join() + + @threading_helper.requires_working_threading() class MonitoringMisc(MonitoringTestMixin, TestCase): def register_callback(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2025-08-06-15-39-54.gh-issue-137400.xIw0zs.rst b/Misc/NEWS.d/next/Core and Builtins/2025-08-06-15-39-54.gh-issue-137400.xIw0zs.rst new file mode 100644 index 00000000000000..a464cf48948f9a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2025-08-06-15-39-54.gh-issue-137400.xIw0zs.rst @@ -0,0 +1,4 @@ +Fix a crash in the :term:`free threading` build when disabling profiling or tracing +across all threads with :c:func:`PyEval_SetProfileAllThreads` or +:c:func:`PyEval_SetTraceAllThreads` or their Python equivalents +:func:`threading.settrace_all_threads` and :func:`threading.setprofile_all_threads`. diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index 8a9ad3601a379e..2f6d43658a9fa4 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -479,13 +479,16 @@ setup_profile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject } } + _PyEval_StopTheWorld(tstate->interp); int delta = (func != NULL) - (tstate->c_profilefunc != NULL); tstate->c_profilefunc = func; *old_profileobj = tstate->c_profileobj; tstate->c_profileobj = Py_XNewRef(arg); tstate->interp->sys_profiling_threads += delta; assert(tstate->interp->sys_profiling_threads >= 0); - return tstate->interp->sys_profiling_threads; + Py_ssize_t profiling_threads = tstate->interp->sys_profiling_threads; + _PyEval_StartTheWorld(tstate->interp); + return profiling_threads; } int @@ -574,13 +577,16 @@ setup_tracing(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject } } + _PyEval_StopTheWorld(tstate->interp); int delta = (func != NULL) - (tstate->c_tracefunc != NULL); tstate->c_tracefunc = func; *old_traceobj = tstate->c_traceobj; tstate->c_traceobj = Py_XNewRef(arg); tstate->interp->sys_tracing_threads += delta; assert(tstate->interp->sys_tracing_threads >= 0); - return tstate->interp->sys_tracing_threads; + Py_ssize_t tracing_threads = tstate->interp->sys_tracing_threads; + _PyEval_StartTheWorld(tstate->interp); + return tracing_threads; } int _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: arch...@mail-archive.com