gh-59956: Clarify Runtime State Status Expectations (gh-101308)

A PyThreadState can be in one of many states in its lifecycle, represented by some status value.  Those statuses haven't been particularly clear, so we're addressing that here.  Specifically:

* made the distinct lifecycle statuses clear on PyThreadState
* identified expectations of how various lifecycle-related functions relate to status
* noted the various places where those expectations don't match the actual behavior

At some point we'll need to address the mismatches.

(This change also includes some cleanup.)

https://github.com/python/cpython/issues/59956
This commit is contained in:
Eric Snow 2023-01-30 12:07:48 -07:00 committed by GitHub
parent ea232716d3
commit e11fc032a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 426 additions and 176 deletions

View File

@ -119,7 +119,30 @@ struct _ts {
PyThreadState *next;
PyInterpreterState *interp;
int _status;
struct {
/* Has been initialized to a safe state.
In order to be effective, this must be set to 0 during or right
after allocation. */
unsigned int initialized:1;
/* Has been bound to an OS thread. */
unsigned int bound:1;
/* Has been unbound from its OS thread. */
unsigned int unbound:1;
/* Has been bound aa current for the GILState API. */
unsigned int bound_gilstate:1;
/* Currently in use (maybe holds the GIL). */
unsigned int active:1;
/* various stages of finalization */
unsigned int finalizing:1;
unsigned int cleared:1;
unsigned int finalized:1;
/* padding to align to 4 bytes */
unsigned int :24;
} _status;
int py_recursion_remaining;
int py_recursion_limit;
@ -245,6 +268,8 @@ struct _ts {
// Alias for backward compatibility with Python 3.8
#define _PyInterpreterState_Get PyInterpreterState_Get
/* An alias for the internal _PyThreadState_New(),
kept for stable ABI compatibility. */
PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *);
/* Similar to PyThreadState_Get(), but don't issue a fatal error

View File

@ -120,13 +120,12 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) {
// PyThreadState functions
PyAPI_FUNC(PyThreadState *) _PyThreadState_New(PyInterpreterState *interp);
PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate);
// We keep this around exclusively for stable ABI compatibility.
PyAPI_FUNC(void) _PyThreadState_Init(
PyThreadState *tstate);
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(
_PyRuntimeState *runtime,
PyThreadState *tstate);
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
static inline void
@ -139,18 +138,6 @@ _PyThreadState_UpdateTracingState(PyThreadState *tstate)
}
/* PyThreadState status */
#define PyThreadState_UNINITIALIZED 0
/* Has been initialized to a safe state.
In order to be effective, this must be set to 0 during or right
after allocation. */
#define PyThreadState_INITIALIZED 1
#define PyThreadState_BOUND 2
#define PyThreadState_UNBOUND 3
/* Other */
PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap(

View File

@ -1161,7 +1161,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
return PyErr_NoMemory();
}
boot->interp = _PyInterpreterState_GET();
boot->tstate = _PyThreadState_Prealloc(boot->interp);
boot->tstate = _PyThreadState_New(boot->interp);
if (boot->tstate == NULL) {
PyMem_Free(boot);
if (!PyErr_Occurred()) {

View File

@ -624,7 +624,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate)
}
/* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(runtime, tstate);
_PyThreadState_DeleteExcept(tstate);
return _PyStatus_OK();
}
#endif

View File

@ -696,10 +696,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
init_interp_settings(interp, &config);
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState *tstate = _PyThreadState_New(interp);
if (tstate == NULL) {
return _PyStatus_ERR("can't make first thread");
}
_PyThreadState_Bind(tstate);
(void) PyThreadState_Swap(tstate);
status = init_interp_create_gil(tstate);
@ -1821,6 +1822,11 @@ Py_FinalizeEx(void)
/* Get current thread state and interpreter pointer */
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
// XXX assert(_Py_IsMainInterpreter(tstate->interp));
// XXX assert(_Py_IsMainThread());
// Block some operations.
tstate->interp->finalizing = 1;
// Wrap up existing "threading"-module-created, non-daemon threads.
wait_for_thread_shutdown(tstate);
@ -1867,7 +1873,23 @@ Py_FinalizeEx(void)
_PyRuntimeState_SetFinalizing() has been called, no other Python thread
can take the GIL at this point: if they try, they will exit
immediately. */
_PyThreadState_DeleteExcept(runtime, tstate);
_PyThreadState_DeleteExcept(tstate);
/* At this point no Python code should be running at all.
The only thread state left should be the main thread of the main
interpreter (AKA tstate), in which this code is running right now.
There may be other OS threads running but none of them will have
thread states associated with them, nor will be able to create
new thread states.
Thus tstate is the only possible thread state from here on out.
It may still be used during finalization to run Python code as
needed or provide runtime state (e.g. sys.modules) but that will
happen sparingly. Furthermore, the order of finalization aims
to not need a thread (or interpreter) state as soon as possible.
*/
// XXX Make sure we are preventing the creating of any new thread states
// (or interpreters).
/* Flush sys.stdout and sys.stderr */
if (flush_std_files() < 0) {
@ -1958,6 +1980,20 @@ Py_FinalizeEx(void)
}
#endif /* Py_TRACE_REFS */
/* At this point there's almost no other Python code that will run,
nor interpreter state needed. The only possibility is the
finalizers of the objects stored on tstate (and tstate->interp),
which are triggered via finalize_interp_clear().
For now we operate as though none of those finalizers actually
need an operational thread state or interpreter. In reality,
those finalizers may rely on some part of tstate or
tstate->interp, and/or may raise exceptions
or otherwise fail.
*/
// XXX Do this sooner during finalization.
// XXX Ensure finalizer errors are handled properly.
finalize_interp_clear(tstate);
finalize_interp_delete(tstate->interp);
@ -2039,12 +2075,13 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
return _PyStatus_OK();
}
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState *tstate = _PyThreadState_New(interp);
if (tstate == NULL) {
PyInterpreterState_Delete(interp);
*tstate_p = NULL;
return _PyStatus_OK();
}
_PyThreadState_Bind(tstate);
PyThreadState *save_tstate = PyThreadState_Swap(tstate);

View File

@ -37,9 +37,6 @@ to avoid the expense of doing their own locking).
extern "C" {
#endif
/* Forward declarations */
static void _PyThreadState_Delete(PyThreadState *tstate, int check_current);
/****************************************/
/* helpers for the current thread state */
@ -81,69 +78,56 @@ current_fast_clear(_PyRuntimeState *runtime)
_Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)NULL);
}
#define tstate_verify_not_active(tstate) \
if (tstate == current_fast_get((tstate)->interp->runtime)) { \
_Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); \
}
//------------------------------------------------
// the thread state bound to the current OS thread
//------------------------------------------------
/*
The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind().
The GIL does no need to be held for these.
*/
static int
current_tss_initialized(_PyRuntimeState *runtime)
static inline int
tstate_tss_initialized(Py_tss_t *key)
{
return PyThread_tss_is_created(&runtime->autoTSSkey);
}
static PyStatus
current_tss_init(_PyRuntimeState *runtime)
{
assert(!current_tss_initialized(runtime));
if (PyThread_tss_create(&runtime->autoTSSkey) != 0) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
static void
current_tss_fini(_PyRuntimeState *runtime)
{
assert(current_tss_initialized(runtime));
PyThread_tss_delete(&runtime->autoTSSkey);
}
static inline PyThreadState *
current_tss_get(_PyRuntimeState *runtime)
{
assert(current_tss_initialized(runtime));
return (PyThreadState *)PyThread_tss_get(&runtime->autoTSSkey);
return PyThread_tss_is_created(key);
}
static inline int
_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate)
tstate_tss_init(Py_tss_t *key)
{
assert(tstate != NULL);
assert(current_tss_initialized(runtime));
return PyThread_tss_set(&runtime->autoTSSkey, (void *)tstate);
}
static inline void
current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate)
{
if (_current_tss_set(runtime, tstate) != 0) {
Py_FatalError("failed to set current tstate (TSS)");
}
assert(!tstate_tss_initialized(key));
return PyThread_tss_create(key);
}
static inline void
current_tss_clear(_PyRuntimeState *runtime)
tstate_tss_fini(Py_tss_t *key)
{
assert(current_tss_initialized(runtime));
if (PyThread_tss_set(&runtime->autoTSSkey, NULL) != 0) {
Py_FatalError("failed to clear current tstate (TSS)");
}
assert(tstate_tss_initialized(key));
PyThread_tss_delete(key);
}
static inline PyThreadState *
tstate_tss_get(Py_tss_t *key)
{
assert(tstate_tss_initialized(key));
return (PyThreadState *)PyThread_tss_get(key);
}
static inline int
tstate_tss_set(Py_tss_t *key, PyThreadState *tstate)
{
assert(tstate != NULL);
assert(tstate_tss_initialized(key));
return PyThread_tss_set(key, (void *)tstate);
}
static inline int
tstate_tss_clear(Py_tss_t *key)
{
assert(tstate_tss_initialized(key));
return PyThread_tss_set(key, (void *)NULL);
}
#ifdef HAVE_FORK
@ -152,92 +136,178 @@ current_tss_clear(_PyRuntimeState *runtime)
* don't reset TSS upon fork(), see issue #10517.
*/
static PyStatus
current_tss_reinit(_PyRuntimeState *runtime)
tstate_tss_reinit(Py_tss_t *key)
{
if (!current_tss_initialized(runtime)) {
if (!tstate_tss_initialized(key)) {
return _PyStatus_OK();
}
PyThreadState *tstate = current_tss_get(runtime);
PyThreadState *tstate = tstate_tss_get(key);
current_tss_fini(runtime);
PyStatus status = current_tss_init(runtime);
if (_PyStatus_EXCEPTION(status)) {
return status;
tstate_tss_fini(key);
if (tstate_tss_init(key) != 0) {
return _PyStatus_NO_MEMORY();
}
/* If the thread had an associated auto thread state, reassociate it with
* the new key. */
if (tstate && _current_tss_set(runtime, tstate) != 0) {
return _PyStatus_ERR("failed to set autoTSSkey");
if (tstate && tstate_tss_set(key, tstate) != 0) {
return _PyStatus_ERR("failed to re-set autoTSSkey");
}
return _PyStatus_OK();
}
#endif
/*
The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind().
The GIL does no need to be held for these.
*/
#define gilstate_tss_initialized(runtime) \
tstate_tss_initialized(&(runtime)->autoTSSkey)
#define gilstate_tss_init(runtime) \
tstate_tss_init(&(runtime)->autoTSSkey)
#define gilstate_tss_fini(runtime) \
tstate_tss_fini(&(runtime)->autoTSSkey)
#define gilstate_tss_get(runtime) \
tstate_tss_get(&(runtime)->autoTSSkey)
#define _gilstate_tss_set(runtime, tstate) \
tstate_tss_set(&(runtime)->autoTSSkey, tstate)
#define _gilstate_tss_clear(runtime) \
tstate_tss_clear(&(runtime)->autoTSSkey)
#define gilstate_tss_reinit(runtime) \
tstate_tss_reinit(&(runtime)->autoTSSkey)
static inline void
gilstate_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate)
{
assert(tstate != NULL && tstate->interp->runtime == runtime);
if (_gilstate_tss_set(runtime, tstate) != 0) {
Py_FatalError("failed to set current tstate (TSS)");
}
}
static inline void
gilstate_tss_clear(_PyRuntimeState *runtime)
{
if (_gilstate_tss_clear(runtime) != 0) {
Py_FatalError("failed to clear current tstate (TSS)");
}
}
static inline int tstate_is_alive(PyThreadState *tstate);
static inline int
tstate_is_bound(PyThreadState *tstate)
{
return tstate->_status.bound && !tstate->_status.unbound;
}
static void bind_gilstate_tstate(PyThreadState *);
static void unbind_gilstate_tstate(PyThreadState *);
static void
bind_tstate(PyThreadState *tstate)
{
assert(tstate != NULL);
assert(tstate->_status == PyThreadState_INITIALIZED);
assert(tstate_is_alive(tstate) && !tstate->_status.bound);
assert(!tstate->_status.unbound); // just in case
assert(!tstate->_status.bound_gilstate);
assert(tstate != gilstate_tss_get(tstate->interp->runtime));
assert(!tstate->_status.active);
assert(tstate->thread_id == 0);
assert(tstate->native_thread_id == 0);
_PyRuntimeState *runtime = tstate->interp->runtime;
/* Stick the thread state for this thread in thread specific storage.
The only situation where you can legitimately have more than one
thread state for an OS level thread is when there are multiple
interpreters.
You shouldn't really be using the PyGILState_ APIs anyway (see issues
#10915 and #15751).
The first thread state created for that given OS level thread will
"win", which seems reasonable behaviour.
*/
/* When a thread state is created for a thread by some mechanism
other than PyGILState_Ensure(), it's important that the GILState
machinery knows about it so it doesn't try to create another
thread state for the thread.
(This is a better fix for SF bug #1010677 than the first one attempted.)
*/
// XXX Skipping like this does not play nice with multiple interpreters.
if (current_tss_get(runtime) == NULL) {
current_tss_set(runtime, tstate);
}
// Currently we don't necessarily store the thread state
// in thread-local storage (e.g. per-interpreter).
tstate->thread_id = PyThread_get_thread_ident();
#ifdef PY_HAVE_THREAD_NATIVE_ID
tstate->native_thread_id = PyThread_get_thread_native_id();
#endif
tstate->_status = PyThreadState_BOUND;
tstate->_status.bound = 1;
}
static void
unbind_tstate(PyThreadState *tstate)
{
assert(tstate != NULL);
assert(tstate->_status == PyThreadState_BOUND);
// XXX assert(tstate_is_alive(tstate));
assert(tstate_is_bound(tstate));
// XXX assert(!tstate->_status.active);
assert(tstate->thread_id > 0);
#ifdef PY_HAVE_THREAD_NATIVE_ID
assert(tstate->native_thread_id > 0);
#endif
_PyRuntimeState *runtime = tstate->interp->runtime;
if (current_tss_initialized(runtime) &&
tstate == current_tss_get(runtime))
{
current_tss_clear(runtime);
}
// We leave thread_id and native_thread_id alone
// since they can be useful for debugging.
// Check the `_status` field to know if these values
// are still valid.
tstate->_status = PyThreadState_UNBOUND;
// We leave tstate->_status.bound set to 1
// to indicate it was previously bound.
tstate->_status.unbound = 1;
}
/* Stick the thread state for this thread in thread specific storage.
When a thread state is created for a thread by some mechanism
other than PyGILState_Ensure(), it's important that the GILState
machinery knows about it so it doesn't try to create another
thread state for the thread.
(This is a better fix for SF bug #1010677 than the first one attempted.)
The only situation where you can legitimately have more than one
thread state for an OS level thread is when there are multiple
interpreters.
The PyGILState_*() APIs don't work with multiple
interpreters (see bpo-10915 and bpo-15751), so this function
sets TSS only once. Thus, the first thread state created for that
given OS level thread will "win", which seems reasonable behaviour.
*/
static void
bind_gilstate_tstate(PyThreadState *tstate)
{
assert(tstate != NULL);
assert(tstate_is_alive(tstate));
assert(tstate_is_bound(tstate));
// XXX assert(!tstate->_status.active);
assert(!tstate->_status.bound_gilstate);
_PyRuntimeState *runtime = tstate->interp->runtime;
PyThreadState *tcur = gilstate_tss_get(runtime);
assert(tstate != tcur);
if (tcur != NULL) {
// The original gilstate implementation only respects the
// first thread state set.
// XXX Skipping like this does not play nice with multiple interpreters.
return;
tcur->_status.bound_gilstate = 0;
}
gilstate_tss_set(runtime, tstate);
tstate->_status.bound_gilstate = 1;
}
static void
unbind_gilstate_tstate(PyThreadState *tstate)
{
assert(tstate != NULL);
// XXX assert(tstate_is_alive(tstate));
assert(tstate_is_bound(tstate));
// XXX assert(!tstate->_status.active);
assert(tstate->_status.bound_gilstate);
assert(tstate == gilstate_tss_get(tstate->interp->runtime));
gilstate_tss_clear(tstate->interp->runtime);
tstate->_status.bound_gilstate = 0;
}
@ -261,7 +331,7 @@ holds_gil(PyThreadState *tstate)
assert(tstate != NULL);
_PyRuntimeState *runtime = tstate->interp->runtime;
/* Must be the tstate for this thread */
assert(tstate == current_tss_get(runtime));
assert(tstate == gilstate_tss_get(runtime));
return tstate == current_fast_get(runtime);
}
@ -394,10 +464,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
memcpy(runtime, &initial, sizeof(*runtime));
}
PyStatus status = current_tss_init(runtime);
if (_PyStatus_EXCEPTION(status)) {
if (gilstate_tss_init(runtime) != 0) {
_PyRuntimeState_Fini(runtime);
return status;
return _PyStatus_NO_MEMORY();
}
if (PyThread_tss_create(&runtime->trashTSSkey) != 0) {
@ -414,8 +483,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
if (current_tss_initialized(runtime)) {
current_tss_fini(runtime);
if (gilstate_tss_initialized(runtime)) {
gilstate_tss_fini(runtime);
}
if (PyThread_tss_is_created(&runtime->trashTSSkey)) {
@ -475,7 +544,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
}
PyStatus status = current_tss_reinit(runtime);
PyStatus status = gilstate_tss_reinit(runtime);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
@ -669,18 +738,38 @@ error:
static void
interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
{
assert(interp != NULL);
assert(tstate != NULL);
_PyRuntimeState *runtime = interp->runtime;
/* XXX Conditions we need to enforce:
* the GIL must be held by the current thread
* tstate must be the "current" thread state (current_fast_get())
* tstate->interp must be interp
* for the main interpreter, tstate must be the main thread
*/
// XXX Ideally, we would not rely on any thread state in this function
// (and we would drop the "tstate" argument).
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) {
_PyErr_Clear(tstate);
}
HEAD_LOCK(runtime);
// XXX Clear the current/main thread state last.
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
PyThreadState_Clear(p);
}
HEAD_UNLOCK(runtime);
/* It is possible that any of the objects below have a finalizer
that runs Python code or otherwise relies on a thread state
or even the interpreter state. For now we trust that isn't
a problem.
*/
// XXX Make sure we properly deal with problematic finalizers.
Py_CLEAR(interp->audit_hooks);
PyConfig_Clear(&interp->config);
@ -751,7 +840,6 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
// garbage. It can be different than the current Python thread state
// of 'interp'.
PyThreadState *current_tstate = current_fast_get(interp->runtime);
interpreter_clear(interp, current_tstate);
}
@ -763,30 +851,26 @@ _PyInterpreterState_Clear(PyThreadState *tstate)
}
static void
zapthreads(PyInterpreterState *interp, int check_current)
{
PyThreadState *tstate;
/* No need to lock the mutex here because this should only happen
when the threads are all really dead (XXX famous last words). */
while ((tstate = interp->threads.head) != NULL) {
_PyThreadState_Delete(tstate, check_current);
}
}
static void zapthreads(PyInterpreterState *interp);
void
PyInterpreterState_Delete(PyInterpreterState *interp)
{
_PyRuntimeState *runtime = interp->runtime;
struct pyinterpreters *interpreters = &runtime->interpreters;
zapthreads(interp, 0);
// XXX Clearing the "current" thread state should happen before
// we start finalizing the interpreter (or the current thread state).
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);
}
zapthreads(interp);
_PyEval_FiniState(&interp->ceval);
/* Delete current thread. After this, many C API calls become crashy. */
_PyThreadState_Swap(runtime, NULL);
HEAD_LOCK(runtime);
PyInterpreterState **p;
for (p = &interpreters->head; ; p = &(*p)->next) {
@ -843,8 +927,10 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
continue;
}
// XXX Won't this fail since PyInterpreterState_Clear() requires
// the "current" tstate to be set?
PyInterpreterState_Clear(interp); // XXX must activate?
zapthreads(interp, 1);
zapthreads(interp);
if (interp->id_mutex != NULL) {
PyThread_free_lock(interp->id_mutex);
}
@ -1062,6 +1148,16 @@ _PyInterpreterState_LookUpID(int64_t requested_id)
/* the per-thread runtime state */
/********************************/
static inline int
tstate_is_alive(PyThreadState *tstate)
{
return (tstate->_status.initialized &&
!tstate->_status.finalized &&
!tstate->_status.cleared &&
!tstate->_status.finalizing);
}
//----------
// lifecycle
//----------
@ -1112,7 +1208,7 @@ init_threadstate(PyThreadState *tstate,
PyInterpreterState *interp, uint64_t id,
PyThreadState *next)
{
if (tstate->_status != PyThreadState_UNINITIALIZED) {
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
}
@ -1148,7 +1244,7 @@ init_threadstate(PyThreadState *tstate,
tstate->datastack_top = NULL;
tstate->datastack_limit = NULL;
tstate->_status = PyThreadState_INITIALIZED;
tstate->_status.initialized = 1;
}
static PyThreadState *
@ -1208,17 +1304,29 @@ PyThreadState_New(PyInterpreterState *interp)
PyThreadState *tstate = new_threadstate(interp);
if (tstate) {
bind_tstate(tstate);
// This makes sure there's a gilstate tstate bound
// as soon as possible.
if (gilstate_tss_get(tstate->interp->runtime) == NULL) {
bind_gilstate_tstate(tstate);
}
}
return tstate;
}
// This must be followed by a call to _PyThreadState_Bind();
PyThreadState *
_PyThreadState_Prealloc(PyInterpreterState *interp)
_PyThreadState_New(PyInterpreterState *interp)
{
return new_threadstate(interp);
}
// We keep this for stable ABI compabibility.
PyThreadState *
_PyThreadState_Prealloc(PyInterpreterState *interp)
{
return _PyThreadState_New(interp);
}
// We keep this around for (accidental) stable ABI compatibility.
// Realistically, no extensions are using it.
void
@ -1230,6 +1338,17 @@ _PyThreadState_Init(PyThreadState *tstate)
void
PyThreadState_Clear(PyThreadState *tstate)
{
assert(tstate->_status.initialized && !tstate->_status.cleared);
// XXX assert(!tstate->_status.bound || tstate->_status.unbound);
tstate->_status.finalizing = 1; // just in case
/* XXX Conditions we need to enforce:
* the GIL must be held by the current thread
* current_fast_get()->interp must match tstate->interp
* for the main interpreter, current_fast_get() must be the main thread
*/
int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose;
if (verbose && tstate->cframe->current_frame != NULL) {
@ -1244,6 +1363,17 @@ PyThreadState_Clear(PyThreadState *tstate)
"PyThreadState_Clear: warning: thread still has a frame\n");
}
/* At this point tstate shouldn't be used any more,
neither to run Python code nor for other uses.
This is tricky when current_fast_get() == tstate, in the same way
as noted in interpreter_clear() above. The below finalizers
can possibly run Python code or otherwise use the partially
cleared thread state. For now we trust that isn't a problem
in practice.
*/
// XXX Deal with the possibility of problematic finalizers.
/* Don't clear tstate->pyframe: it is a borrowed reference */
Py_CLEAR(tstate->dict);
@ -1274,6 +1404,11 @@ PyThreadState_Clear(PyThreadState *tstate)
if (tstate->on_delete != NULL) {
tstate->on_delete(tstate->on_delete_data);
}
tstate->_status.cleared = 1;
// XXX Call _PyThreadStateSwap(runtime, NULL) here if "current".
// XXX Do it as early in the function as possible.
}
@ -1281,7 +1416,8 @@ PyThreadState_Clear(PyThreadState *tstate)
static void
tstate_delete_common(PyThreadState *tstate)
{
_Py_EnsureTstateNotNULL(tstate);
assert(tstate->_status.cleared && !tstate->_status.finalized);
PyInterpreterState *interp = tstate->interp;
if (interp == NULL) {
Py_FatalError("NULL interpreter");
@ -1300,7 +1436,11 @@ tstate_delete_common(PyThreadState *tstate)
}
HEAD_UNLOCK(runtime);
// XXX Do this in PyThreadState_Swap() (and assert not-equal here)?
// XXX Unbind in PyThreadState_Clear(), or earlier
// (and assert not-equal here)?
if (tstate->_status.bound_gilstate) {
unbind_gilstate_tstate(tstate);
}
unbind_tstate(tstate);
// XXX Move to PyThreadState_Clear()?
@ -1311,25 +1451,32 @@ tstate_delete_common(PyThreadState *tstate)
_PyObject_VirtualFree(chunk, chunk->size);
chunk = prev;
}
tstate->_status.finalized = 1;
}
static void
_PyThreadState_Delete(PyThreadState *tstate, int check_current)
zapthreads(PyInterpreterState *interp)
{
if (check_current) {
if (tstate == current_fast_get(tstate->interp->runtime)) {
_Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate);
}
PyThreadState *tstate;
/* No need to lock the mutex here because this should only happen
when the threads are all really dead (XXX famous last words). */
while ((tstate = interp->threads.head) != NULL) {
tstate_verify_not_active(tstate);
tstate_delete_common(tstate);
free_threadstate(tstate);
}
tstate_delete_common(tstate);
free_threadstate(tstate);
}
void
PyThreadState_Delete(PyThreadState *tstate)
{
_PyThreadState_Delete(tstate, 1);
_Py_EnsureTstateNotNULL(tstate);
tstate_verify_not_active(tstate);
tstate_delete_common(tstate);
free_threadstate(tstate);
}
@ -1359,9 +1506,11 @@ PyThreadState_DeleteCurrent(void)
* be kept in those other interpreters.
*/
void
_PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate)
_PyThreadState_DeleteExcept(PyThreadState *tstate)
{
assert(tstate != NULL);
PyInterpreterState *interp = tstate->interp;
_PyRuntimeState *runtime = interp->runtime;
HEAD_LOCK(runtime);
/* Remove all thread states, except tstate, from the linked list of
@ -1460,6 +1609,38 @@ PyThreadState_GetID(PyThreadState *tstate)
}
static inline void
tstate_activate(PyThreadState *tstate)
{
assert(tstate != NULL);
// XXX assert(tstate_is_alive(tstate));
assert(tstate_is_bound(tstate));
assert(!tstate->_status.active);
assert(!tstate->_status.bound_gilstate ||
tstate == gilstate_tss_get((tstate->interp->runtime)));
if (!tstate->_status.bound_gilstate) {
bind_gilstate_tstate(tstate);
}
tstate->_status.active = 1;
}
static inline void
tstate_deactivate(PyThreadState *tstate)
{
assert(tstate != NULL);
// XXX assert(tstate_is_alive(tstate));
assert(tstate_is_bound(tstate));
assert(tstate->_status.active);
tstate->_status.active = 0;
// We do not unbind the gilstate tstate here.
// It will still be used in PyGILState_Ensure().
}
//----------
// other API
//----------
@ -1535,31 +1716,43 @@ PyThreadState_Get(void)
PyThreadState *
_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
{
#if defined(Py_DEBUG)
/* This can be called from PyEval_RestoreThread(). Similar
to it, we need to ensure errno doesn't change.
*/
int err = errno;
#endif
PyThreadState *oldts = current_fast_get(runtime);
if (newts == NULL) {
current_fast_clear(runtime);
current_fast_clear(runtime);
if (oldts != NULL) {
// XXX assert(tstate_is_alive(oldts) && tstate_is_bound(oldts));
tstate_deactivate(oldts);
}
else {
if (newts != NULL) {
// XXX assert(tstate_is_alive(newts));
assert(tstate_is_bound(newts));
current_fast_set(runtime, newts);
tstate_activate(newts);
}
/* It should not be possible for more than one thread state
to be used for a thread. Check this the best we can in debug
builds.
*/
// XXX The above isn't true when multiple interpreters are involved.
#if defined(Py_DEBUG)
if (newts && current_tss_initialized(runtime)) {
/* This can be called from PyEval_RestoreThread(). Similar
to it, we need to ensure errno doesn't change.
*/
int err = errno;
PyThreadState *check = current_tss_get(runtime);
if (check && check->interp == newts->interp && check != newts) {
Py_FatalError("Invalid thread state for this thread");
if (newts && gilstate_tss_initialized(runtime)) {
PyThreadState *check = gilstate_tss_get(runtime);
if (check != newts) {
if (check && check->interp == newts->interp) {
Py_FatalError("Invalid thread state for this thread");
}
}
errno = err;
}
errno = err;
#endif
return oldts;
}
@ -1575,6 +1768,11 @@ void
_PyThreadState_Bind(PyThreadState *tstate)
{
bind_tstate(tstate);
// This makes sure there's a gilstate tstate bound
// as soon as possible.
if (gilstate_tss_get(tstate->interp->runtime) == NULL) {
bind_gilstate_tstate(tstate);
}
}
@ -1863,7 +2061,7 @@ _PyGILState_Init(PyInterpreterState *interp)
return _PyStatus_OK();
}
_PyRuntimeState *runtime = interp->runtime;
assert(current_tss_get(runtime) == NULL);
assert(gilstate_tss_get(runtime) == NULL);
assert(runtime->gilstate.autoInterpreterState == NULL);
runtime->gilstate.autoInterpreterState = interp;
return _PyStatus_OK();
@ -1899,7 +2097,7 @@ _PyGILState_SetTstate(PyThreadState *tstate)
_PyRuntimeState *runtime = tstate->interp->runtime;
assert(runtime->gilstate.autoInterpreterState == tstate->interp);
assert(current_tss_get(runtime) == tstate);
assert(gilstate_tss_get(runtime) == tstate);
assert(tstate->gilstate_counter == 1);
#endif
@ -1918,10 +2116,10 @@ PyThreadState *
PyGILState_GetThisThreadState(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
if (!current_tss_initialized(runtime)) {
if (!gilstate_tss_initialized(runtime)) {
return NULL;
}
return current_tss_get(runtime);
return gilstate_tss_get(runtime);
}
int
@ -1932,7 +2130,7 @@ PyGILState_Check(void)
return 1;
}
if (!current_tss_initialized(runtime)) {
if (!gilstate_tss_initialized(runtime)) {
return 1;
}
@ -1941,7 +2139,7 @@ PyGILState_Check(void)
return 0;
}
return (tstate == current_tss_get(runtime));
return (tstate == gilstate_tss_get(runtime));
}
PyGILState_STATE
@ -1957,17 +2155,19 @@ PyGILState_Ensure(void)
/* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been
called by Py_Initialize() */
assert(_PyEval_ThreadsInitialized(runtime));
assert(current_tss_initialized(runtime));
assert(gilstate_tss_initialized(runtime));
assert(runtime->gilstate.autoInterpreterState != NULL);
PyThreadState *tcur = current_tss_get(runtime);
PyThreadState *tcur = gilstate_tss_get(runtime);
int has_gil;
if (tcur == NULL) {
/* Create a new Python thread state for this thread */
tcur = PyThreadState_New(runtime->gilstate.autoInterpreterState);
tcur = new_threadstate(runtime->gilstate.autoInterpreterState);
if (tcur == NULL) {
Py_FatalError("Couldn't create thread-state for new thread");
}
bind_tstate(tcur);
bind_gilstate_tstate(tcur);
/* This is our thread state! We'll need to delete it in the
matching call to PyGILState_Release(). */
@ -1997,7 +2197,7 @@ void
PyGILState_Release(PyGILState_STATE oldstate)
{
_PyRuntimeState *runtime = &_PyRuntime;
PyThreadState *tstate = current_tss_get(runtime);
PyThreadState *tstate = gilstate_tss_get(runtime);
if (tstate == NULL) {
Py_FatalError("auto-releasing thread-state, "
"but no thread-state for this thread");
@ -2023,6 +2223,7 @@ PyGILState_Release(PyGILState_STATE oldstate)
if (tstate->gilstate_counter == 0) {
/* can't have been locked when we created it */
assert(oldstate == PyGILState_UNLOCKED);
// XXX Unbind tstate here.
PyThreadState_Clear(tstate);
/* Delete the thread-state. Note this releases the GIL too!
* It's vital that the GIL be held here, to avoid shutdown