Skip to content

Commit 12287aa

Browse files
Add bind_gilstate_tstate() and unbind_gilstate_tstate().
1 parent 8145caa commit 12287aa

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

Include/cpython/pystate.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ struct _ts {
130130
unsigned int bound:1;
131131
/* Has been unbound from its OS thread. */
132132
unsigned int unbound:1;
133+
/* Has been bound aa current for the GILState API. */
134+
unsigned int bound_gilstate:1;
133135
/* Currently in use (maybe holds the GIL). */
134136
unsigned int active:1;
135137

@@ -139,7 +141,7 @@ struct _ts {
139141
unsigned int finalized:1;
140142

141143
/* padding to align to 4 bytes */
142-
unsigned int :26;
144+
unsigned int :25;
143145
} _status;
144146

145147
int py_recursion_remaining;

Python/pystate.c

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,73 @@ tstate_is_bound(PyThreadState *tstate)
205205
return tstate->_status.bound && !tstate->_status.unbound;
206206
}
207207

208+
static void bind_gilstate_tstate(PyThreadState *);
209+
static void unbind_gilstate_tstate(PyThreadState *);
210+
208211
static void
209212
bind_tstate(PyThreadState *tstate)
210213
{
211214
assert(tstate != NULL);
212215
assert(tstate_is_alive(tstate) && !tstate->_status.bound);
213216
assert(!tstate->_status.unbound); // just in case
217+
assert(!tstate->_status.bound_gilstate);
218+
assert(tstate != gilstate_tss_get(tstate->interp->runtime));
214219
assert(!tstate->_status.active);
215220
assert(tstate->thread_id == 0);
216221
assert(tstate->native_thread_id == 0);
222+
223+
// Currently we don't necessarily store the thread state
224+
// in thread-local storage (e.g. per-interpreter).
225+
226+
tstate->thread_id = PyThread_get_thread_ident();
227+
#ifdef PY_HAVE_THREAD_NATIVE_ID
228+
tstate->native_thread_id = PyThread_get_thread_native_id();
229+
#endif
230+
231+
tstate->_status.bound = 1;
232+
233+
// This make sure there's a gilstate tstate bound
234+
// as soon as possible.
235+
if (gilstate_tss_get(tstate->interp->runtime) == NULL) {
236+
bind_gilstate_tstate(tstate);
237+
}
238+
}
239+
240+
static void
241+
unbind_tstate(PyThreadState *tstate)
242+
{
243+
assert(tstate != NULL);
244+
// XXX assert(tstate_is_alive(tstate));
245+
assert(tstate_is_bound(tstate));
246+
// XXX assert(!tstate->_status.active);
247+
assert(tstate->thread_id > 0);
248+
#ifdef PY_HAVE_THREAD_NATIVE_ID
249+
assert(tstate->native_thread_id > 0);
250+
#endif
251+
252+
if (tstate->_status.bound_gilstate) {
253+
unbind_gilstate_tstate(tstate);
254+
}
255+
256+
// We leave thread_id and native_thraed_id alone
257+
// since they can be useful for debugging.
258+
// Check the `_status` field to know if these values
259+
// are still valid.
260+
261+
// We leave tstate->_status.bound set to 1
262+
// to indicate it was previously bound.
263+
tstate->_status.unbound = 1;
264+
}
265+
266+
267+
static void
268+
bind_gilstate_tstate(PyThreadState *tstate)
269+
{
270+
assert(tstate != NULL);
271+
assert(tstate_is_alive(tstate));
272+
assert(tstate_is_bound(tstate));
273+
// XXX assert(!tstate->_status.active);
274+
// XXX assert(!tstate->_status.bound_gilstate);
217275
_PyRuntimeState *runtime = tstate->interp->runtime;
218276

219277
/* Stick the thread state for this thread in thread specific storage.
@@ -238,42 +296,24 @@ bind_tstate(PyThreadState *tstate)
238296
if (gilstate_tss_get(runtime) == NULL) {
239297
gilstate_tss_set(runtime, tstate);
240298
}
241-
242-
tstate->thread_id = PyThread_get_thread_ident();
243-
#ifdef PY_HAVE_THREAD_NATIVE_ID
244-
tstate->native_thread_id = PyThread_get_thread_native_id();
245-
#endif
246-
247-
tstate->_status.bound = 1;
299+
tstate->_status.bound_gilstate = 1;
248300
}
249301

250302
static void
251-
unbind_tstate(PyThreadState *tstate)
303+
unbind_gilstate_tstate(PyThreadState *tstate)
252304
{
253305
assert(tstate != NULL);
306+
// XXX assert(tstate_is_alive(tstate));
254307
assert(tstate_is_bound(tstate));
255-
// XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
256308
// XXX assert(!tstate->_status.active);
257-
assert(tstate->thread_id > 0);
258-
#ifdef PY_HAVE_THREAD_NATIVE_ID
259-
assert(tstate->native_thread_id > 0);
260-
#endif
261-
_PyRuntimeState *runtime = tstate->interp->runtime;
309+
assert(tstate->_status.bound_gilstate);
310+
// XXX assert(tstate == gilstate_tss_get(tstate->interp->runtime));
262311

263-
if (gilstate_tss_initialized(runtime) &&
264-
tstate == gilstate_tss_get(runtime))
265-
{
266-
gilstate_tss_clear(runtime);
312+
// XXX This check *should* always succeed.
313+
if (tstate == gilstate_tss_get(tstate->interp->runtime)) {
314+
gilstate_tss_clear(tstate->interp->runtime);
267315
}
268-
269-
// We leave thread_id and native_thraed_id alone
270-
// since they can be useful for debugging.
271-
// Check the `_status` field to know if these values
272-
// are still valid.
273-
274-
// We leave tstate->_status.bound set to 1
275-
// to indicate it was previously bound.
276-
tstate->_status.unbound = 1;
316+
tstate->_status.bound_gilstate = 0;
277317
}
278318

279319

@@ -1560,6 +1600,11 @@ tstate_activate(PyThreadState *tstate)
15601600
assert(tstate != NULL);
15611601
assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
15621602
assert(!tstate->_status.active);
1603+
1604+
if (!tstate->_status.bound_gilstate) {
1605+
bind_gilstate_tstate(tstate);
1606+
}
1607+
15631608
tstate->_status.active = 1;
15641609
}
15651610

@@ -1570,7 +1615,11 @@ tstate_deactivate(PyThreadState *tstate)
15701615
assert(tstate_is_bound(tstate));
15711616
// XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
15721617
assert(tstate->_status.active);
1618+
15731619
tstate->_status.active = 0;
1620+
1621+
// We do not unbind the gilstate tstate here.
1622+
// It will still be used in PyGILState_Ensure().
15741623
}
15751624

15761625

@@ -2091,6 +2140,7 @@ PyGILState_Ensure(void)
20912140
Py_FatalError("Couldn't create thread-state for new thread");
20922141
}
20932142
bind_tstate(tcur);
2143+
bind_gilstate_tstate(tcur);
20942144

20952145
/* This is our thread state! We'll need to delete it in the
20962146
matching call to PyGILState_Release(). */
@@ -2146,6 +2196,7 @@ PyGILState_Release(PyGILState_STATE oldstate)
21462196
if (tstate->gilstate_counter == 0) {
21472197
/* can't have been locked when we created it */
21482198
assert(oldstate == PyGILState_UNLOCKED);
2199+
// XXX Unbind tstate here.
21492200
PyThreadState_Clear(tstate);
21502201
/* Delete the thread-state. Note this releases the GIL too!
21512202
* It's vital that the GIL be held here, to avoid shutdown

0 commit comments

Comments
 (0)