https://github.com/python/cpython/commit/e236568c0ea301591ca1517d39ce337e6ee0a48f
commit: e236568c0ea301591ca1517d39ce337e6ee0a48f
branch: 3.13
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]