Skip to content

Commit 4b952e0

Browse files
Factor out bind_tstate() and unbind_tstate().
1 parent 7bdf5a4 commit 4b952e0

File tree

3 files changed

+71
-54
lines changed

3 files changed

+71
-54
lines changed

Include/internal/pycore_pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) {
120120

121121
// PyThreadState functions
122122

123-
PyAPI_FUNC(void) _PyThreadState_SetCurrent(PyThreadState *tstate);
123+
PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate);
124124
// We keep this around exclusively for stable ABI compatibility.
125125
PyAPI_FUNC(void) _PyThreadState_Init(
126126
PyThreadState *tstate);

Modules/_threadmodule.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,13 +1074,7 @@ thread_run(void *boot_raw)
10741074
PyThreadState *tstate;
10751075

10761076
tstate = boot->tstate;
1077-
tstate->thread_id = PyThread_get_thread_ident();
1078-
#ifdef PY_HAVE_THREAD_NATIVE_ID
1079-
tstate->native_thread_id = PyThread_get_thread_native_id();
1080-
#else
1081-
tstate->native_thread_id = 0;
1082-
#endif
1083-
_PyThreadState_SetCurrent(tstate);
1077+
_PyThreadState_Bind(tstate);
10841078
PyEval_AcquireThread(tstate);
10851079
tstate->interp->threads.count++;
10861080

Python/pystate.c

Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ extern "C" {
3838
#endif
3939

4040
/* Forward declarations */
41-
static void _PyGILState_NoteThreadState(PyThreadState* tstate);
4241
static void _PyThreadState_Delete(PyThreadState *tstate, int check_current);
4342

4443

@@ -149,6 +148,64 @@ current_tss_reinit(_PyRuntimeState *runtime)
149148
}
150149
#endif
151150

151+
static void
152+
bind_tstate(PyThreadState *tstate)
153+
{
154+
assert(tstate != NULL);
155+
assert(tstate->thread_id == 0);
156+
assert(tstate->native_thread_id == 0);
157+
_PyRuntimeState *runtime = tstate->interp->runtime;
158+
159+
/* Stick the thread state for this thread in thread specific storage.
160+
161+
The only situation where you can legitimately have more than one
162+
thread state for an OS level thread is when there are multiple
163+
interpreters.
164+
165+
You shouldn't really be using the PyGILState_ APIs anyway (see issues
166+
#10915 and #15751).
167+
168+
The first thread state created for that given OS level thread will
169+
"win", which seems reasonable behaviour.
170+
*/
171+
/* When a thread state is created for a thread by some mechanism
172+
other than PyGILState_Ensure(), it's important that the GILState
173+
machinery knows about it so it doesn't try to create another
174+
thread state for the thread.
175+
(This is a better fix for SF bug #1010677 than the first one attempted.)
176+
*/
177+
// XXX Skipping like this does not play nice with multiple interpreters.
178+
if (current_tss_get(runtime) == NULL) {
179+
current_tss_set(runtime, tstate);
180+
}
181+
182+
tstate->thread_id = PyThread_get_thread_ident();
183+
#ifdef PY_HAVE_THREAD_NATIVE_ID
184+
tstate->native_thread_id = PyThread_get_thread_native_id();
185+
#endif
186+
}
187+
188+
static void
189+
unbind_tstate(PyThreadState *tstate)
190+
{
191+
assert(tstate != NULL);
192+
assert(tstate->thread_id > 0);
193+
#ifdef PY_HAVE_THREAD_NATIVE_ID
194+
assert(tstate->native_thread_id > 0);
195+
#endif
196+
_PyRuntimeState *runtime = tstate->interp->runtime;
197+
198+
if (current_tss_initialized(runtime) &&
199+
tstate == current_tss_get(runtime))
200+
{
201+
current_tss_clear(runtime);
202+
}
203+
204+
// -1 makes sure the thread state won't be re-bound.
205+
tstate->thread_id = -1;
206+
tstate->native_thread_id = -1;
207+
}
208+
152209

153210
/* the global runtime */
154211

@@ -926,17 +983,18 @@ init_threadstate(PyThreadState *tstate,
926983
tstate->next = next;
927984
assert(tstate->prev == NULL);
928985

929-
tstate->thread_id = PyThread_get_thread_ident();
930-
#ifdef PY_HAVE_THREAD_NATIVE_ID
931-
tstate->native_thread_id = PyThread_get_thread_native_id();
932-
#endif
986+
// thread_id and native_thread_id are set in bind_tstate().
933987

934988
tstate->py_recursion_limit = interp->ceval.recursion_limit,
935989
tstate->py_recursion_remaining = interp->ceval.recursion_limit,
936990
tstate->c_recursion_remaining = C_RECURSION_LIMIT;
937991

938992
tstate->exc_info = &tstate->exc_state;
939993

994+
// PyGILState_Release must not try to delete this thread state.
995+
// This is cleared when PyGILState_Ensure() creates the thread sate.
996+
tstate->gilstate_counter = 1;
997+
940998
tstate->cframe = &tstate->root_cframe;
941999
tstate->datastack_chunk = NULL;
9421000
tstate->datastack_top = NULL;
@@ -1001,11 +1059,12 @@ PyThreadState_New(PyInterpreterState *interp)
10011059
{
10021060
PyThreadState *tstate = new_threadstate(interp);
10031061
if (tstate) {
1004-
_PyThreadState_SetCurrent(tstate);
1062+
bind_tstate(tstate);
10051063
}
10061064
return tstate;
10071065
}
10081066

1067+
// This must be followed by a call to _PyThreadState_Bind();
10091068
PyThreadState *
10101069
_PyThreadState_Prealloc(PyInterpreterState *interp)
10111070
{
@@ -1021,10 +1080,9 @@ _PyThreadState_Init(PyThreadState *tstate)
10211080
}
10221081

10231082
void
1024-
_PyThreadState_SetCurrent(PyThreadState *tstate)
1083+
_PyThreadState_Bind(PyThreadState *tstate)
10251084
{
1026-
assert(tstate != NULL);
1027-
_PyGILState_NoteThreadState(tstate);
1085+
bind_tstate(tstate);
10281086
}
10291087

10301088
PyObject*
@@ -1229,11 +1287,7 @@ tstate_delete_common(PyThreadState *tstate)
12291287
HEAD_UNLOCK(runtime);
12301288

12311289
// XXX Do this in PyThreadState_Swap() (and assert not-equal here)?
1232-
if (current_tss_initialized(runtime) &&
1233-
tstate == current_tss_get(runtime))
1234-
{
1235-
current_tss_clear(runtime);
1236-
}
1290+
unbind_tstate(tstate);
12371291

12381292
// XXX Move to PyThreadState_Clear()?
12391293
_PyStackChunk *chunk = tstate->datastack_chunk;
@@ -1714,38 +1768,6 @@ _PyGILState_GetInterpreterStateUnsafe(void)
17141768
return _PyRuntime.gilstate.autoInterpreterState;
17151769
}
17161770

1717-
/* When a thread state is created for a thread by some mechanism other than
1718-
PyGILState_Ensure, it's important that the GILState machinery knows about
1719-
it so it doesn't try to create another thread state for the thread (this is
1720-
a better fix for SF bug #1010677 than the first one attempted).
1721-
*/
1722-
static void
1723-
_PyGILState_NoteThreadState(PyThreadState* tstate)
1724-
{
1725-
assert(tstate != NULL);
1726-
_PyRuntimeState *runtime = tstate->interp->runtime;
1727-
assert(current_tss_initialized(runtime));
1728-
1729-
/* Stick the thread state for this thread in thread specific storage.
1730-
1731-
The only situation where you can legitimately have more than one
1732-
thread state for an OS level thread is when there are multiple
1733-
interpreters.
1734-
1735-
You shouldn't really be using the PyGILState_ APIs anyway (see issues
1736-
#10915 and #15751).
1737-
1738-
The first thread state created for that given OS level thread will
1739-
"win", which seems reasonable behaviour.
1740-
*/
1741-
if (current_tss_get(runtime) == NULL) {
1742-
current_tss_set(runtime, tstate);
1743-
}
1744-
1745-
/* PyGILState_Release must not try to delete this thread state. */
1746-
tstate->gilstate_counter = 1;
1747-
}
1748-
17491771
/* The public functions */
17501772

17511773
PyThreadState *
@@ -1805,6 +1827,7 @@ PyGILState_Ensure(void)
18051827

18061828
/* This is our thread state! We'll need to delete it in the
18071829
matching call to PyGILState_Release(). */
1830+
assert(tcur->gilstate_counter == 1);
18081831
tcur->gilstate_counter = 0;
18091832
current = 0; /* new thread state is never current */
18101833
}

0 commit comments

Comments
 (0)