@@ -205,15 +205,73 @@ tstate_is_bound(PyThreadState *tstate)
205
205
return tstate -> _status .bound && !tstate -> _status .unbound ;
206
206
}
207
207
208
+ static void bind_gilstate_tstate (PyThreadState * );
209
+ static void unbind_gilstate_tstate (PyThreadState * );
210
+
208
211
static void
209
212
bind_tstate (PyThreadState * tstate )
210
213
{
211
214
assert (tstate != NULL );
212
215
assert (tstate_is_alive (tstate ) && !tstate -> _status .bound );
213
216
assert (!tstate -> _status .unbound ); // just in case
217
+ assert (!tstate -> _status .bound_gilstate );
218
+ assert (tstate != gilstate_tss_get (tstate -> interp -> runtime ));
214
219
assert (!tstate -> _status .active );
215
220
assert (tstate -> thread_id == 0 );
216
221
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);
217
275
_PyRuntimeState * runtime = tstate -> interp -> runtime ;
218
276
219
277
/* Stick the thread state for this thread in thread specific storage.
@@ -238,42 +296,24 @@ bind_tstate(PyThreadState *tstate)
238
296
if (gilstate_tss_get (runtime ) == NULL ) {
239
297
gilstate_tss_set (runtime , tstate );
240
298
}
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 ;
248
300
}
249
301
250
302
static void
251
- unbind_tstate (PyThreadState * tstate )
303
+ unbind_gilstate_tstate (PyThreadState * tstate )
252
304
{
253
305
assert (tstate != NULL );
306
+ // XXX assert(tstate_is_alive(tstate));
254
307
assert (tstate_is_bound (tstate ));
255
- // XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
256
308
// 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));
262
311
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 );
267
315
}
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 ;
277
317
}
278
318
279
319
@@ -1560,6 +1600,11 @@ tstate_activate(PyThreadState *tstate)
1560
1600
assert (tstate != NULL );
1561
1601
assert (tstate_is_alive (tstate ) && tstate_is_bound (tstate ));
1562
1602
assert (!tstate -> _status .active );
1603
+
1604
+ if (!tstate -> _status .bound_gilstate ) {
1605
+ bind_gilstate_tstate (tstate );
1606
+ }
1607
+
1563
1608
tstate -> _status .active = 1 ;
1564
1609
}
1565
1610
@@ -1570,7 +1615,11 @@ tstate_deactivate(PyThreadState *tstate)
1570
1615
assert (tstate_is_bound (tstate ));
1571
1616
// XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
1572
1617
assert (tstate -> _status .active );
1618
+
1573
1619
tstate -> _status .active = 0 ;
1620
+
1621
+ // We do not unbind the gilstate tstate here.
1622
+ // It will still be used in PyGILState_Ensure().
1574
1623
}
1575
1624
1576
1625
@@ -2091,6 +2140,7 @@ PyGILState_Ensure(void)
2091
2140
Py_FatalError ("Couldn't create thread-state for new thread" );
2092
2141
}
2093
2142
bind_tstate (tcur );
2143
+ bind_gilstate_tstate (tcur );
2094
2144
2095
2145
/* This is our thread state! We'll need to delete it in the
2096
2146
matching call to PyGILState_Release(). */
@@ -2146,6 +2196,7 @@ PyGILState_Release(PyGILState_STATE oldstate)
2146
2196
if (tstate -> gilstate_counter == 0 ) {
2147
2197
/* can't have been locked when we created it */
2148
2198
assert (oldstate == PyGILState_UNLOCKED );
2199
+ // XXX Unbind tstate here.
2149
2200
PyThreadState_Clear (tstate );
2150
2201
/* Delete the thread-state. Note this releases the GIL too!
2151
2202
* It's vital that the GIL be held here, to avoid shutdown
0 commit comments