mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
gh-98257: Make _PyEval_SetTrace() reentrant (#98258)
Make sys.setprofile() and sys.settrace() functions reentrant. They can no long fail with: RuntimeError("Cannot install a trace function while another trace function is being installed"). Make _PyEval_SetTrace() and _PyEval_SetProfile() functions reentrant, rather than detecting and rejecting reentrant calls. Only delete the reference to function arguments once the new function is fully set, when a reentrant call is safe. Call also _PySys_Audit() earlier.
This commit is contained in:
parent
4bd63f66cd
commit
a8fe4bbd6b
4 changed files with 20 additions and 56 deletions
|
@ -6343,38 +6343,23 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|||
/* The caller must hold the GIL */
|
||||
assert(PyGILState_Check());
|
||||
|
||||
static int reentrant = 0;
|
||||
if (reentrant) {
|
||||
_PyErr_SetString(tstate, PyExc_RuntimeError, "Cannot install a profile function "
|
||||
"while another profile function is being installed");
|
||||
reentrant = 0;
|
||||
return -1;
|
||||
}
|
||||
reentrant = 1;
|
||||
|
||||
/* Call _PySys_Audit() in the context of the current thread state,
|
||||
even if tstate is not the current thread state. */
|
||||
PyThreadState *current_tstate = _PyThreadState_GET();
|
||||
if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
|
||||
reentrant = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *profileobj = tstate->c_profileobj;
|
||||
|
||||
tstate->c_profilefunc = NULL;
|
||||
tstate->c_profileobj = NULL;
|
||||
/* Must make sure that tracing is not ignored if 'profileobj' is freed */
|
||||
_PyThreadState_UpdateTracingState(tstate);
|
||||
Py_XDECREF(profileobj);
|
||||
|
||||
Py_XINCREF(arg);
|
||||
tstate->c_profileobj = arg;
|
||||
tstate->c_profilefunc = func;
|
||||
|
||||
PyObject *old_profileobj = tstate->c_profileobj;
|
||||
tstate->c_profileobj = Py_XNewRef(arg);
|
||||
/* Flag that tracing or profiling is turned on */
|
||||
_PyThreadState_UpdateTracingState(tstate);
|
||||
reentrant = 0;
|
||||
|
||||
// gh-98257: Only call Py_XDECREF() once the new profile function is fully
|
||||
// set, so it's safe to call sys.setprofile() again (reentrant call).
|
||||
Py_XDECREF(old_profileobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -6416,39 +6401,23 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|||
/* The caller must hold the GIL */
|
||||
assert(PyGILState_Check());
|
||||
|
||||
static int reentrant = 0;
|
||||
|
||||
if (reentrant) {
|
||||
_PyErr_SetString(tstate, PyExc_RuntimeError, "Cannot install a trace function "
|
||||
"while another trace function is being installed");
|
||||
reentrant = 0;
|
||||
return -1;
|
||||
}
|
||||
reentrant = 1;
|
||||
|
||||
/* Call _PySys_Audit() in the context of the current thread state,
|
||||
even if tstate is not the current thread state. */
|
||||
PyThreadState *current_tstate = _PyThreadState_GET();
|
||||
if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
|
||||
reentrant = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *traceobj = tstate->c_traceobj;
|
||||
|
||||
tstate->c_tracefunc = NULL;
|
||||
tstate->c_traceobj = NULL;
|
||||
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
|
||||
_PyThreadState_UpdateTracingState(tstate);
|
||||
Py_XINCREF(arg);
|
||||
Py_XDECREF(traceobj);
|
||||
tstate->c_traceobj = arg;
|
||||
tstate->c_tracefunc = func;
|
||||
|
||||
PyObject *old_traceobj = tstate->c_traceobj;
|
||||
tstate->c_traceobj = Py_XNewRef(arg);
|
||||
/* Flag that tracing or profiling is turned on */
|
||||
_PyThreadState_UpdateTracingState(tstate);
|
||||
|
||||
reentrant = 0;
|
||||
// gh-98257: Only call Py_XDECREF() once the new trace function is fully
|
||||
// set, so it's safe to call sys.settrace() again (reentrant call).
|
||||
Py_XDECREF(old_traceobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue