summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/cpython/pystate.h27
-rw-r--r--Include/internal/pycore_pystate.h17
-rw-r--r--Modules/_threadmodule.c2
-rw-r--r--Python/ceval_gil.c2
-rw-r--r--Python/pylifecycle.c43
-rw-r--r--Python/pystate.c491
6 files changed, 416 insertions, 166 deletions
diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h
index 3c1e70d..f5db52f 100644
--- a/Include/cpython/pystate.h
+++ b/Include/cpython/pystate.h
@@ -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
diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h
index 0e46693..7046ec8 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -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(
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index bf4b6ec..9c12c69 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -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()) {
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index 73d412b..1bf2233 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -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
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 5ef2d3f..a8a8e7f 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -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);
diff --git a/Python/pystate.c b/Python/pystate.c
index bf7688f..8bb49d9 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -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);
+ return PyThread_tss_is_created(key);
}
-static PyStatus
-current_tss_init(_PyRuntimeState *runtime)
+static inline int
+tstate_tss_init(Py_tss_t *key)
{
- assert(!current_tss_initialized(runtime));
- if (PyThread_tss_create(&runtime->autoTSSkey) != 0) {
- return _PyStatus_NO_MEMORY();
- }
- return _PyStatus_OK();
+ assert(!tstate_tss_initialized(key));
+ return PyThread_tss_create(key);
}
-static void
-current_tss_fini(_PyRuntimeState *runtime)
+static inline void
+tstate_tss_fini(Py_tss_t *key)
{
- assert(current_tss_initialized(runtime));
- PyThread_tss_delete(&runtime->autoTSSkey);
+ assert(tstate_tss_initialized(key));
+ PyThread_tss_delete(key);
}
static inline PyThreadState *
-current_tss_get(_PyRuntimeState *runtime)
+tstate_tss_get(Py_tss_t *key)
{
- assert(current_tss_initialized(runtime));
- return (PyThreadState *)PyThread_tss_get(&runtime->autoTSSkey);
+ assert(tstate_tss_initialized(key));
+ return (PyThreadState *)PyThread_tss_get(key);
}
static inline int
-_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate)
+tstate_tss_set(Py_tss_t *key, PyThreadState *tstate)
{
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_set(key, (void *)tstate);
}
-static inline void
-current_tss_clear(_PyRuntimeState *runtime)
+static inline int
+tstate_tss_clear(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));
+ 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,29 +851,25 @@ _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);
- _PyEval_FiniState(&interp->ceval);
+ // 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);
- /* Delete current thread. After this, many C API calls become crashy. */
- _PyThreadState_Swap(runtime, NULL);
+ _PyEval_FiniState(&interp->ceval);
HEAD_LOCK(runtime);
PyInterpreterState **p;
@@ -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