gh-104341: Call _PyEval_ReleaseLock() with NULL When Finalizing the Current Thread (gh-105109)

This avoids the problematic race in drop_gil() by skipping the FORCE_SWITCHING code there for finalizing threads.

(The idea for this approach came out of discussions with @markshannon.)
This commit is contained in:
Eric Snow 2023-06-01 16:24:10 -06:00 committed by GitHub
parent 8a8ebf2e3d
commit 3698fda06e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 9 deletions

View file

@ -822,6 +822,12 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
p = p->next;
HEAD_UNLOCK(runtime);
}
if (tstate->interp == interp) {
/* We fix tstate->_status below when we for sure aren't using it
(e.g. no longer need the GIL). */
// XXX Eliminate the need to do this.
tstate->_status.cleared = 0;
}
/* It is possible that any of the objects below have a finalizer
that runs Python code or otherwise relies on a thread state
@ -886,6 +892,12 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
Py_CLEAR(interp->builtins);
Py_CLEAR(interp->interpreter_trampoline);
if (tstate->interp == interp) {
/* We are now safe to fix tstate->_status.cleared. */
// XXX Do this (much) earlier?
tstate->_status.cleared = 1;
}
for (int i=0; i < DICT_MAX_WATCHERS; i++) {
interp->dict_state.watchers[i] = NULL;
}
@ -930,6 +942,7 @@ _PyInterpreterState_Clear(PyThreadState *tstate)
}
static inline void tstate_deactivate(PyThreadState *tstate);
static void zapthreads(PyInterpreterState *interp);
void
@ -943,7 +956,9 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
PyThreadState *tcur = current_fast_get(runtime);
if (tcur != NULL && interp == tcur->interp) {
/* Unset current thread. After this, many C API calls become crashy. */
_PyThreadState_Swap(runtime, NULL);
current_fast_clear(runtime);
tstate_deactivate(tcur);
_PyEval_ReleaseLock(interp, NULL);
}
zapthreads(interp);
@ -1567,7 +1582,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate)
_Py_EnsureTstateNotNULL(tstate);
tstate_delete_common(tstate);
current_fast_clear(tstate->interp->runtime);
_PyEval_ReleaseLock(tstate);
_PyEval_ReleaseLock(tstate->interp, NULL);
free_threadstate(tstate);
}
@ -1907,7 +1922,7 @@ _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
{
PyThreadState *oldts = current_fast_get(runtime);
if (oldts != NULL) {
_PyEval_ReleaseLock(oldts);
_PyEval_ReleaseLock(oldts->interp, oldts);
}
_swap_thread_states(runtime, oldts, newts);
if (newts != NULL) {