mirror of
https://github.com/python/cpython.git
synced 2026-02-13 19:04:37 +00:00
gh-104690: thread_run() checks for tstate dangling pointer (#109056)
thread_run() of _threadmodule.c now calls
_PyThreadState_CheckConsistency() to check if tstate is a dangling
pointer when Python is built in debug mode.
Rename ceval_gil.c is_tstate_valid() to
_PyThreadState_CheckConsistency() to reuse it in _threadmodule.c.
(cherry picked from commit f63d37877a)
This commit is contained in:
parent
9dd28d2da9
commit
b55cf2c2d8
5 changed files with 37 additions and 26 deletions
|
|
@ -216,20 +216,6 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
|
|||
"cannot access free variable '%s' where it is not associated with a" \
|
||||
" value in enclosing scope"
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
|
||||
PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen
|
||||
when a thread continues to run after Python finalization, especially
|
||||
daemon threads. */
|
||||
static int
|
||||
is_tstate_valid(PyThreadState *tstate)
|
||||
{
|
||||
assert(!_PyMem_IsPtrFreed(tstate));
|
||||
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* This can set eval_breaker to 0 even though gil_drop_request became
|
||||
1. We believe this is all right because the eval loop will release
|
||||
|
|
@ -464,7 +450,7 @@ PyEval_AcquireThread(PyThreadState *tstate)
|
|||
void
|
||||
PyEval_ReleaseThread(PyThreadState *tstate)
|
||||
{
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
||||
PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
|
||||
|
|
@ -671,7 +657,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
|||
static int
|
||||
handle_signals(PyThreadState *tstate)
|
||||
{
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -739,7 +725,7 @@ void
|
|||
_Py_FinishPendingCalls(PyThreadState *tstate)
|
||||
{
|
||||
assert(PyGILState_Check());
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
struct _pending_calls *pending = &tstate->interp->ceval.pending;
|
||||
|
||||
|
|
@ -764,7 +750,7 @@ Py_MakePendingCalls(void)
|
|||
assert(PyGILState_Check());
|
||||
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
/* Python signal handler doesn't really queue a callback: it only signals
|
||||
that a signal was received, see _PyEval_SignalReceived(). */
|
||||
|
|
@ -6947,7 +6933,7 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
|
|||
int
|
||||
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||
{
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
/* The caller must hold the GIL */
|
||||
assert(PyGILState_Check());
|
||||
|
||||
|
|
@ -6999,7 +6985,7 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
|
|||
int
|
||||
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||
{
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
/* The caller must hold the GIL */
|
||||
assert(PyGILState_Check());
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
|
|||
/* Not switched yet => wait */
|
||||
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
||||
{
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
RESET_GIL_DROP_REQUEST(tstate->interp);
|
||||
/* NOTE: if COND_WAIT does not atomically start waiting when
|
||||
releasing the mutex, another thread can run through, take
|
||||
|
|
@ -226,7 +226,7 @@ take_gil(PyThreadState *tstate)
|
|||
PyThread_exit_thread();
|
||||
}
|
||||
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
|
|
@ -268,7 +268,7 @@ take_gil(PyThreadState *tstate)
|
|||
}
|
||||
PyThread_exit_thread();
|
||||
}
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
SET_GIL_DROP_REQUEST(interp);
|
||||
drop_requested = 1;
|
||||
|
|
@ -307,7 +307,7 @@ take_gil(PyThreadState *tstate)
|
|||
drop_gil(ceval, ceval2, tstate);
|
||||
PyThread_exit_thread();
|
||||
}
|
||||
assert(is_tstate_valid(tstate));
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request)) {
|
||||
RESET_GIL_DROP_REQUEST(interp);
|
||||
|
|
|
|||
|
|
@ -2237,6 +2237,24 @@ _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame * frame)
|
|||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that a Python thread state valid. In practice, this function is used
|
||||
// on a Python debug build to check if 'tstate' is a dangling pointer, if the
|
||||
// PyThreadState memory has been freed.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// assert(_PyThreadState_CheckConsistency(tstate));
|
||||
int
|
||||
_PyThreadState_CheckConsistency(PyThreadState *tstate)
|
||||
{
|
||||
assert(!_PyMem_IsPtrFreed(tstate));
|
||||
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue