mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
[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 362692852f)
This commit is contained in:
parent
63a9494878
commit
e236568c0e
3 changed files with 47 additions and 2 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
environment to verify things are thread-safe in a free-threaded build"""
|
environment to verify things are thread-safe in a free-threaded build"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
import weakref
|
import weakref
|
||||||
|
|
@ -192,6 +193,40 @@ def during_threads(self):
|
||||||
self.set = not self.set
|
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()
|
@threading_helper.requires_working_threading()
|
||||||
class MonitoringMisc(MonitoringTestMixin, TestCase):
|
class MonitoringMisc(MonitoringTestMixin, TestCase):
|
||||||
def register_callback(self):
|
def register_callback(self):
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
@ -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);
|
int delta = (func != NULL) - (tstate->c_profilefunc != NULL);
|
||||||
tstate->c_profilefunc = func;
|
tstate->c_profilefunc = func;
|
||||||
*old_profileobj = tstate->c_profileobj;
|
*old_profileobj = tstate->c_profileobj;
|
||||||
tstate->c_profileobj = Py_XNewRef(arg);
|
tstate->c_profileobj = Py_XNewRef(arg);
|
||||||
tstate->interp->sys_profiling_threads += delta;
|
tstate->interp->sys_profiling_threads += delta;
|
||||||
assert(tstate->interp->sys_profiling_threads >= 0);
|
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
|
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);
|
int delta = (func != NULL) - (tstate->c_tracefunc != NULL);
|
||||||
tstate->c_tracefunc = func;
|
tstate->c_tracefunc = func;
|
||||||
*old_traceobj = tstate->c_traceobj;
|
*old_traceobj = tstate->c_traceobj;
|
||||||
tstate->c_traceobj = Py_XNewRef(arg);
|
tstate->c_traceobj = Py_XNewRef(arg);
|
||||||
tstate->interp->sys_tracing_threads += delta;
|
tstate->interp->sys_tracing_threads += delta;
|
||||||
assert(tstate->interp->sys_tracing_threads >= 0);
|
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
|
int
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue