mirror of https://github.com/python/cpython.git
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:
parent
ea232716d3
commit
e11fc032a7
|
@ -119,7 +119,30 @@ struct _ts {
|
||||||
PyThreadState *next;
|
PyThreadState *next;
|
||||||
PyInterpreterState *interp;
|
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_remaining;
|
||||||
int py_recursion_limit;
|
int py_recursion_limit;
|
||||||
|
@ -245,6 +268,8 @@ struct _ts {
|
||||||
// Alias for backward compatibility with Python 3.8
|
// Alias for backward compatibility with Python 3.8
|
||||||
#define _PyInterpreterState_Get PyInterpreterState_Get
|
#define _PyInterpreterState_Get PyInterpreterState_Get
|
||||||
|
|
||||||
|
/* An alias for the internal _PyThreadState_New(),
|
||||||
|
kept for stable ABI compatibility. */
|
||||||
PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *);
|
PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *);
|
||||||
|
|
||||||
/* Similar to PyThreadState_Get(), but don't issue a fatal error
|
/* Similar to PyThreadState_Get(), but don't issue a fatal error
|
||||||
|
|
|
@ -120,13 +120,12 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) {
|
||||||
|
|
||||||
// PyThreadState functions
|
// PyThreadState functions
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyThreadState *) _PyThreadState_New(PyInterpreterState *interp);
|
||||||
PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate);
|
PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate);
|
||||||
// We keep this around exclusively for stable ABI compatibility.
|
// We keep this around exclusively for stable ABI compatibility.
|
||||||
PyAPI_FUNC(void) _PyThreadState_Init(
|
PyAPI_FUNC(void) _PyThreadState_Init(
|
||||||
PyThreadState *tstate);
|
PyThreadState *tstate);
|
||||||
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(
|
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
|
||||||
_PyRuntimeState *runtime,
|
|
||||||
PyThreadState *tstate);
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
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 */
|
/* Other */
|
||||||
|
|
||||||
PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap(
|
PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap(
|
||||||
|
|
|
@ -1161,7 +1161,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
}
|
}
|
||||||
boot->interp = _PyInterpreterState_GET();
|
boot->interp = _PyInterpreterState_GET();
|
||||||
boot->tstate = _PyThreadState_Prealloc(boot->interp);
|
boot->tstate = _PyThreadState_New(boot->interp);
|
||||||
if (boot->tstate == NULL) {
|
if (boot->tstate == NULL) {
|
||||||
PyMem_Free(boot);
|
PyMem_Free(boot);
|
||||||
if (!PyErr_Occurred()) {
|
if (!PyErr_Occurred()) {
|
||||||
|
|
|
@ -624,7 +624,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destroy all threads except the current one */
|
/* Destroy all threads except the current one */
|
||||||
_PyThreadState_DeleteExcept(runtime, tstate);
|
_PyThreadState_DeleteExcept(tstate);
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -696,10 +696,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
|
||||||
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
|
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
|
||||||
init_interp_settings(interp, &config);
|
init_interp_settings(interp, &config);
|
||||||
|
|
||||||
PyThreadState *tstate = PyThreadState_New(interp);
|
PyThreadState *tstate = _PyThreadState_New(interp);
|
||||||
if (tstate == NULL) {
|
if (tstate == NULL) {
|
||||||
return _PyStatus_ERR("can't make first thread");
|
return _PyStatus_ERR("can't make first thread");
|
||||||
}
|
}
|
||||||
|
_PyThreadState_Bind(tstate);
|
||||||
(void) PyThreadState_Swap(tstate);
|
(void) PyThreadState_Swap(tstate);
|
||||||
|
|
||||||
status = init_interp_create_gil(tstate);
|
status = init_interp_create_gil(tstate);
|
||||||
|
@ -1821,6 +1822,11 @@ Py_FinalizeEx(void)
|
||||||
|
|
||||||
/* Get current thread state and interpreter pointer */
|
/* Get current thread state and interpreter pointer */
|
||||||
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
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.
|
// Wrap up existing "threading"-module-created, non-daemon threads.
|
||||||
wait_for_thread_shutdown(tstate);
|
wait_for_thread_shutdown(tstate);
|
||||||
|
@ -1867,7 +1873,23 @@ Py_FinalizeEx(void)
|
||||||
_PyRuntimeState_SetFinalizing() has been called, no other Python thread
|
_PyRuntimeState_SetFinalizing() has been called, no other Python thread
|
||||||
can take the GIL at this point: if they try, they will exit
|
can take the GIL at this point: if they try, they will exit
|
||||||
immediately. */
|
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 */
|
/* Flush sys.stdout and sys.stderr */
|
||||||
if (flush_std_files() < 0) {
|
if (flush_std_files() < 0) {
|
||||||
|
@ -1958,6 +1980,20 @@ Py_FinalizeEx(void)
|
||||||
}
|
}
|
||||||
#endif /* Py_TRACE_REFS */
|
#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_clear(tstate);
|
||||||
finalize_interp_delete(tstate->interp);
|
finalize_interp_delete(tstate->interp);
|
||||||
|
|
||||||
|
@ -2039,12 +2075,13 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
PyThreadState *tstate = PyThreadState_New(interp);
|
PyThreadState *tstate = _PyThreadState_New(interp);
|
||||||
if (tstate == NULL) {
|
if (tstate == NULL) {
|
||||||
PyInterpreterState_Delete(interp);
|
PyInterpreterState_Delete(interp);
|
||||||
*tstate_p = NULL;
|
*tstate_p = NULL;
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
|
_PyThreadState_Bind(tstate);
|
||||||
|
|
||||||
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
|
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
|
||||||
|
|
||||||
|
|
511
Python/pystate.c
511
Python/pystate.c
|
@ -37,9 +37,6 @@ to avoid the expense of doing their own locking).
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
static void _PyThreadState_Delete(PyThreadState *tstate, int check_current);
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************/
|
/****************************************/
|
||||||
/* helpers for the current thread state */
|
/* 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);
|
_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 thread state bound to the current OS thread
|
||||||
//------------------------------------------------
|
//------------------------------------------------
|
||||||
|
|
||||||
/*
|
static inline int
|
||||||
The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind().
|
tstate_tss_initialized(Py_tss_t *key)
|
||||||
|
|
||||||
The GIL does no need to be held for these.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
current_tss_initialized(_PyRuntimeState *runtime)
|
|
||||||
{
|
{
|
||||||
return PyThread_tss_is_created(&runtime->autoTSSkey);
|
return PyThread_tss_is_created(key);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate)
|
tstate_tss_init(Py_tss_t *key)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(!tstate_tss_initialized(key));
|
||||||
assert(current_tss_initialized(runtime));
|
return PyThread_tss_create(key);
|
||||||
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)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
current_tss_clear(_PyRuntimeState *runtime)
|
tstate_tss_fini(Py_tss_t *key)
|
||||||
{
|
{
|
||||||
assert(current_tss_initialized(runtime));
|
assert(tstate_tss_initialized(key));
|
||||||
if (PyThread_tss_set(&runtime->autoTSSkey, NULL) != 0) {
|
PyThread_tss_delete(key);
|
||||||
Py_FatalError("failed to clear current tstate (TSS)");
|
}
|
||||||
}
|
|
||||||
|
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
|
#ifdef HAVE_FORK
|
||||||
|
@ -152,92 +136,178 @@ current_tss_clear(_PyRuntimeState *runtime)
|
||||||
* don't reset TSS upon fork(), see issue #10517.
|
* don't reset TSS upon fork(), see issue #10517.
|
||||||
*/
|
*/
|
||||||
static PyStatus
|
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();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
PyThreadState *tstate = current_tss_get(runtime);
|
PyThreadState *tstate = tstate_tss_get(key);
|
||||||
|
|
||||||
current_tss_fini(runtime);
|
tstate_tss_fini(key);
|
||||||
PyStatus status = current_tss_init(runtime);
|
if (tstate_tss_init(key) != 0) {
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
return _PyStatus_NO_MEMORY();
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the thread had an associated auto thread state, reassociate it with
|
/* If the thread had an associated auto thread state, reassociate it with
|
||||||
* the new key. */
|
* the new key. */
|
||||||
if (tstate && _current_tss_set(runtime, tstate) != 0) {
|
if (tstate && tstate_tss_set(key, tstate) != 0) {
|
||||||
return _PyStatus_ERR("failed to set autoTSSkey");
|
return _PyStatus_ERR("failed to re-set autoTSSkey");
|
||||||
}
|
}
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
static void
|
||||||
bind_tstate(PyThreadState *tstate)
|
bind_tstate(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
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->thread_id == 0);
|
||||||
assert(tstate->native_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.
|
// Currently we don't necessarily store the thread state
|
||||||
|
// in thread-local storage (e.g. per-interpreter).
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
tstate->thread_id = PyThread_get_thread_ident();
|
tstate->thread_id = PyThread_get_thread_ident();
|
||||||
#ifdef PY_HAVE_THREAD_NATIVE_ID
|
#ifdef PY_HAVE_THREAD_NATIVE_ID
|
||||||
tstate->native_thread_id = PyThread_get_thread_native_id();
|
tstate->native_thread_id = PyThread_get_thread_native_id();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tstate->_status = PyThreadState_BOUND;
|
tstate->_status.bound = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
unbind_tstate(PyThreadState *tstate)
|
unbind_tstate(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
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);
|
assert(tstate->thread_id > 0);
|
||||||
#ifdef PY_HAVE_THREAD_NATIVE_ID
|
#ifdef PY_HAVE_THREAD_NATIVE_ID
|
||||||
assert(tstate->native_thread_id > 0);
|
assert(tstate->native_thread_id > 0);
|
||||||
#endif
|
#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
|
// We leave thread_id and native_thread_id alone
|
||||||
// since they can be useful for debugging.
|
// since they can be useful for debugging.
|
||||||
// Check the `_status` field to know if these values
|
// Check the `_status` field to know if these values
|
||||||
// are still valid.
|
// 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);
|
assert(tstate != NULL);
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
_PyRuntimeState *runtime = tstate->interp->runtime;
|
||||||
/* Must be the tstate for this thread */
|
/* 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);
|
return tstate == current_fast_get(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,10 +464,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
||||||
memcpy(runtime, &initial, sizeof(*runtime));
|
memcpy(runtime, &initial, sizeof(*runtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
PyStatus status = current_tss_init(runtime);
|
if (gilstate_tss_init(runtime) != 0) {
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
|
||||||
_PyRuntimeState_Fini(runtime);
|
_PyRuntimeState_Fini(runtime);
|
||||||
return status;
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyThread_tss_create(&runtime->trashTSSkey) != 0) {
|
if (PyThread_tss_create(&runtime->trashTSSkey) != 0) {
|
||||||
|
@ -414,8 +483,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
||||||
void
|
void
|
||||||
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
||||||
{
|
{
|
||||||
if (current_tss_initialized(runtime)) {
|
if (gilstate_tss_initialized(runtime)) {
|
||||||
current_tss_fini(runtime);
|
gilstate_tss_fini(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyThread_tss_is_created(&runtime->trashTSSkey)) {
|
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)) {
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -669,18 +738,38 @@ PyInterpreterState_New(void)
|
||||||
static void
|
static void
|
||||||
interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
|
assert(interp != NULL);
|
||||||
|
assert(tstate != NULL);
|
||||||
_PyRuntimeState *runtime = interp->runtime;
|
_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) {
|
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) {
|
||||||
_PyErr_Clear(tstate);
|
_PyErr_Clear(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
HEAD_LOCK(runtime);
|
HEAD_LOCK(runtime);
|
||||||
|
// XXX Clear the current/main thread state last.
|
||||||
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
|
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
|
||||||
PyThreadState_Clear(p);
|
PyThreadState_Clear(p);
|
||||||
}
|
}
|
||||||
HEAD_UNLOCK(runtime);
|
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);
|
Py_CLEAR(interp->audit_hooks);
|
||||||
|
|
||||||
PyConfig_Clear(&interp->config);
|
PyConfig_Clear(&interp->config);
|
||||||
|
@ -751,7 +840,6 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
|
||||||
// garbage. It can be different than the current Python thread state
|
// garbage. It can be different than the current Python thread state
|
||||||
// of 'interp'.
|
// of 'interp'.
|
||||||
PyThreadState *current_tstate = current_fast_get(interp->runtime);
|
PyThreadState *current_tstate = current_fast_get(interp->runtime);
|
||||||
|
|
||||||
interpreter_clear(interp, current_tstate);
|
interpreter_clear(interp, current_tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,30 +851,26 @@ _PyInterpreterState_Clear(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void zapthreads(PyInterpreterState *interp);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PyInterpreterState_Delete(PyInterpreterState *interp)
|
PyInterpreterState_Delete(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
_PyRuntimeState *runtime = interp->runtime;
|
_PyRuntimeState *runtime = interp->runtime;
|
||||||
struct pyinterpreters *interpreters = &runtime->interpreters;
|
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);
|
_PyEval_FiniState(&interp->ceval);
|
||||||
|
|
||||||
/* Delete current thread. After this, many C API calls become crashy. */
|
|
||||||
_PyThreadState_Swap(runtime, NULL);
|
|
||||||
|
|
||||||
HEAD_LOCK(runtime);
|
HEAD_LOCK(runtime);
|
||||||
PyInterpreterState **p;
|
PyInterpreterState **p;
|
||||||
for (p = &interpreters->head; ; p = &(*p)->next) {
|
for (p = &interpreters->head; ; p = &(*p)->next) {
|
||||||
|
@ -843,8 +927,10 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX Won't this fail since PyInterpreterState_Clear() requires
|
||||||
|
// the "current" tstate to be set?
|
||||||
PyInterpreterState_Clear(interp); // XXX must activate?
|
PyInterpreterState_Clear(interp); // XXX must activate?
|
||||||
zapthreads(interp, 1);
|
zapthreads(interp);
|
||||||
if (interp->id_mutex != NULL) {
|
if (interp->id_mutex != NULL) {
|
||||||
PyThread_free_lock(interp->id_mutex);
|
PyThread_free_lock(interp->id_mutex);
|
||||||
}
|
}
|
||||||
|
@ -1062,6 +1148,16 @@ _PyInterpreterState_LookUpID(int64_t requested_id)
|
||||||
/* the per-thread runtime state */
|
/* 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
|
// lifecycle
|
||||||
//----------
|
//----------
|
||||||
|
@ -1112,7 +1208,7 @@ init_threadstate(PyThreadState *tstate,
|
||||||
PyInterpreterState *interp, uint64_t id,
|
PyInterpreterState *interp, uint64_t id,
|
||||||
PyThreadState *next)
|
PyThreadState *next)
|
||||||
{
|
{
|
||||||
if (tstate->_status != PyThreadState_UNINITIALIZED) {
|
if (tstate->_status.initialized) {
|
||||||
Py_FatalError("thread state already initialized");
|
Py_FatalError("thread state already initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,7 +1244,7 @@ init_threadstate(PyThreadState *tstate,
|
||||||
tstate->datastack_top = NULL;
|
tstate->datastack_top = NULL;
|
||||||
tstate->datastack_limit = NULL;
|
tstate->datastack_limit = NULL;
|
||||||
|
|
||||||
tstate->_status = PyThreadState_INITIALIZED;
|
tstate->_status.initialized = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyThreadState *
|
static PyThreadState *
|
||||||
|
@ -1208,17 +1304,29 @@ PyThreadState_New(PyInterpreterState *interp)
|
||||||
PyThreadState *tstate = new_threadstate(interp);
|
PyThreadState *tstate = new_threadstate(interp);
|
||||||
if (tstate) {
|
if (tstate) {
|
||||||
bind_tstate(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;
|
return tstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This must be followed by a call to _PyThreadState_Bind();
|
// This must be followed by a call to _PyThreadState_Bind();
|
||||||
PyThreadState *
|
PyThreadState *
|
||||||
_PyThreadState_Prealloc(PyInterpreterState *interp)
|
_PyThreadState_New(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
return new_threadstate(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.
|
// We keep this around for (accidental) stable ABI compatibility.
|
||||||
// Realistically, no extensions are using it.
|
// Realistically, no extensions are using it.
|
||||||
void
|
void
|
||||||
|
@ -1230,6 +1338,17 @@ _PyThreadState_Init(PyThreadState *tstate)
|
||||||
void
|
void
|
||||||
PyThreadState_Clear(PyThreadState *tstate)
|
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;
|
int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose;
|
||||||
|
|
||||||
if (verbose && tstate->cframe->current_frame != NULL) {
|
if (verbose && tstate->cframe->current_frame != NULL) {
|
||||||
|
@ -1244,6 +1363,17 @@ PyThreadState_Clear(PyThreadState *tstate)
|
||||||
"PyThreadState_Clear: warning: thread still has a frame\n");
|
"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 */
|
/* Don't clear tstate->pyframe: it is a borrowed reference */
|
||||||
|
|
||||||
Py_CLEAR(tstate->dict);
|
Py_CLEAR(tstate->dict);
|
||||||
|
@ -1274,6 +1404,11 @@ PyThreadState_Clear(PyThreadState *tstate)
|
||||||
if (tstate->on_delete != NULL) {
|
if (tstate->on_delete != NULL) {
|
||||||
tstate->on_delete(tstate->on_delete_data);
|
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
|
static void
|
||||||
tstate_delete_common(PyThreadState *tstate)
|
tstate_delete_common(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
_Py_EnsureTstateNotNULL(tstate);
|
assert(tstate->_status.cleared && !tstate->_status.finalized);
|
||||||
|
|
||||||
PyInterpreterState *interp = tstate->interp;
|
PyInterpreterState *interp = tstate->interp;
|
||||||
if (interp == NULL) {
|
if (interp == NULL) {
|
||||||
Py_FatalError("NULL interpreter");
|
Py_FatalError("NULL interpreter");
|
||||||
|
@ -1300,7 +1436,11 @@ tstate_delete_common(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
HEAD_UNLOCK(runtime);
|
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);
|
unbind_tstate(tstate);
|
||||||
|
|
||||||
// XXX Move to PyThreadState_Clear()?
|
// XXX Move to PyThreadState_Clear()?
|
||||||
|
@ -1311,25 +1451,32 @@ tstate_delete_common(PyThreadState *tstate)
|
||||||
_PyObject_VirtualFree(chunk, chunk->size);
|
_PyObject_VirtualFree(chunk, chunk->size);
|
||||||
chunk = prev;
|
chunk = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tstate->_status.finalized = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_PyThreadState_Delete(PyThreadState *tstate, int check_current)
|
zapthreads(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
if (check_current) {
|
PyThreadState *tstate;
|
||||||
if (tstate == current_fast_get(tstate->interp->runtime)) {
|
/* No need to lock the mutex here because this should only happen
|
||||||
_Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate);
|
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
|
void
|
||||||
PyThreadState_Delete(PyThreadState *tstate)
|
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.
|
* be kept in those other interpreters.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
_PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate)
|
_PyThreadState_DeleteExcept(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
|
assert(tstate != NULL);
|
||||||
PyInterpreterState *interp = tstate->interp;
|
PyInterpreterState *interp = tstate->interp;
|
||||||
|
_PyRuntimeState *runtime = interp->runtime;
|
||||||
|
|
||||||
HEAD_LOCK(runtime);
|
HEAD_LOCK(runtime);
|
||||||
/* Remove all thread states, except tstate, from the linked list of
|
/* 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
|
// other API
|
||||||
//----------
|
//----------
|
||||||
|
@ -1535,31 +1716,43 @@ PyThreadState_Get(void)
|
||||||
PyThreadState *
|
PyThreadState *
|
||||||
_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts)
|
_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);
|
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);
|
current_fast_set(runtime, newts);
|
||||||
|
tstate_activate(newts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It should not be possible for more than one thread state
|
/* 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
|
to be used for a thread. Check this the best we can in debug
|
||||||
builds.
|
builds.
|
||||||
*/
|
*/
|
||||||
// XXX The above isn't true when multiple interpreters are involved.
|
// XXX The above isn't true when multiple interpreters are involved.
|
||||||
#if defined(Py_DEBUG)
|
#if defined(Py_DEBUG)
|
||||||
if (newts && current_tss_initialized(runtime)) {
|
if (newts && gilstate_tss_initialized(runtime)) {
|
||||||
/* This can be called from PyEval_RestoreThread(). Similar
|
PyThreadState *check = gilstate_tss_get(runtime);
|
||||||
to it, we need to ensure errno doesn't change.
|
if (check != newts) {
|
||||||
*/
|
if (check && check->interp == newts->interp) {
|
||||||
int err = errno;
|
Py_FatalError("Invalid thread state for this thread");
|
||||||
PyThreadState *check = current_tss_get(runtime);
|
}
|
||||||
if (check && check->interp == newts->interp && check != newts) {
|
|
||||||
Py_FatalError("Invalid thread state for this thread");
|
|
||||||
}
|
}
|
||||||
errno = err;
|
|
||||||
}
|
}
|
||||||
|
errno = err;
|
||||||
#endif
|
#endif
|
||||||
return oldts;
|
return oldts;
|
||||||
}
|
}
|
||||||
|
@ -1575,6 +1768,11 @@ void
|
||||||
_PyThreadState_Bind(PyThreadState *tstate)
|
_PyThreadState_Bind(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
bind_tstate(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();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
_PyRuntimeState *runtime = interp->runtime;
|
_PyRuntimeState *runtime = interp->runtime;
|
||||||
assert(current_tss_get(runtime) == NULL);
|
assert(gilstate_tss_get(runtime) == NULL);
|
||||||
assert(runtime->gilstate.autoInterpreterState == NULL);
|
assert(runtime->gilstate.autoInterpreterState == NULL);
|
||||||
runtime->gilstate.autoInterpreterState = interp;
|
runtime->gilstate.autoInterpreterState = interp;
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
|
@ -1899,7 +2097,7 @@ _PyGILState_SetTstate(PyThreadState *tstate)
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
_PyRuntimeState *runtime = tstate->interp->runtime;
|
||||||
|
|
||||||
assert(runtime->gilstate.autoInterpreterState == tstate->interp);
|
assert(runtime->gilstate.autoInterpreterState == tstate->interp);
|
||||||
assert(current_tss_get(runtime) == tstate);
|
assert(gilstate_tss_get(runtime) == tstate);
|
||||||
assert(tstate->gilstate_counter == 1);
|
assert(tstate->gilstate_counter == 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1918,10 +2116,10 @@ PyThreadState *
|
||||||
PyGILState_GetThisThreadState(void)
|
PyGILState_GetThisThreadState(void)
|
||||||
{
|
{
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
if (!current_tss_initialized(runtime)) {
|
if (!gilstate_tss_initialized(runtime)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return current_tss_get(runtime);
|
return gilstate_tss_get(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1932,7 +2130,7 @@ PyGILState_Check(void)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current_tss_initialized(runtime)) {
|
if (!gilstate_tss_initialized(runtime)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1941,7 +2139,7 @@ PyGILState_Check(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (tstate == current_tss_get(runtime));
|
return (tstate == gilstate_tss_get(runtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
PyGILState_STATE
|
PyGILState_STATE
|
||||||
|
@ -1957,17 +2155,19 @@ PyGILState_Ensure(void)
|
||||||
/* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been
|
/* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been
|
||||||
called by Py_Initialize() */
|
called by Py_Initialize() */
|
||||||
assert(_PyEval_ThreadsInitialized(runtime));
|
assert(_PyEval_ThreadsInitialized(runtime));
|
||||||
assert(current_tss_initialized(runtime));
|
assert(gilstate_tss_initialized(runtime));
|
||||||
assert(runtime->gilstate.autoInterpreterState != NULL);
|
assert(runtime->gilstate.autoInterpreterState != NULL);
|
||||||
|
|
||||||
PyThreadState *tcur = current_tss_get(runtime);
|
PyThreadState *tcur = gilstate_tss_get(runtime);
|
||||||
int has_gil;
|
int has_gil;
|
||||||
if (tcur == NULL) {
|
if (tcur == NULL) {
|
||||||
/* Create a new Python thread state for this thread */
|
/* Create a new Python thread state for this thread */
|
||||||
tcur = PyThreadState_New(runtime->gilstate.autoInterpreterState);
|
tcur = new_threadstate(runtime->gilstate.autoInterpreterState);
|
||||||
if (tcur == NULL) {
|
if (tcur == NULL) {
|
||||||
Py_FatalError("Couldn't create thread-state for new thread");
|
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
|
/* This is our thread state! We'll need to delete it in the
|
||||||
matching call to PyGILState_Release(). */
|
matching call to PyGILState_Release(). */
|
||||||
|
@ -1997,7 +2197,7 @@ void
|
||||||
PyGILState_Release(PyGILState_STATE oldstate)
|
PyGILState_Release(PyGILState_STATE oldstate)
|
||||||
{
|
{
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
PyThreadState *tstate = current_tss_get(runtime);
|
PyThreadState *tstate = gilstate_tss_get(runtime);
|
||||||
if (tstate == NULL) {
|
if (tstate == NULL) {
|
||||||
Py_FatalError("auto-releasing thread-state, "
|
Py_FatalError("auto-releasing thread-state, "
|
||||||
"but no thread-state for this thread");
|
"but no thread-state for this thread");
|
||||||
|
@ -2023,6 +2223,7 @@ PyGILState_Release(PyGILState_STATE oldstate)
|
||||||
if (tstate->gilstate_counter == 0) {
|
if (tstate->gilstate_counter == 0) {
|
||||||
/* can't have been locked when we created it */
|
/* can't have been locked when we created it */
|
||||||
assert(oldstate == PyGILState_UNLOCKED);
|
assert(oldstate == PyGILState_UNLOCKED);
|
||||||
|
// XXX Unbind tstate here.
|
||||||
PyThreadState_Clear(tstate);
|
PyThreadState_Clear(tstate);
|
||||||
/* Delete the thread-state. Note this releases the GIL too!
|
/* Delete the thread-state. Note this releases the GIL too!
|
||||||
* It's vital that the GIL be held here, to avoid shutdown
|
* It's vital that the GIL be held here, to avoid shutdown
|
||||||
|
|
Loading…
Reference in New Issue