@@ -1203,8 +1203,35 @@ tstate_delete_common(PyThreadState *tstate,
1203
1203
Py_FatalError ("PyThreadState_Delete: NULL interp" );
1204
1204
}
1205
1205
1206
- _PyEventRC * join_event ;
1206
+ assert (is_current ? tstate -> status == _Py_THREAD_ATTACHED
1207
+ : tstate -> status != _Py_THREAD_ATTACHED );
1208
+
1209
+ // Abandon heaps. After this point we must not allocate any Python objects.
1210
+ if (tstate -> heap_gc -> gcstate == & tstate -> interp -> gc ) {
1211
+ tstate -> heap_gc -> gcstate = NULL ;
1212
+ }
1213
+ if (tstate -> heap_obj -> thread_id == _Py_ThreadId () &&
1214
+ _Py_IsMainInterpreter (tstate )) {
1215
+ // NOTE: this may be called from a different thread. This can
1216
+ // happend during shutdown of the interpreter or after forking.
1217
+ // In these cases we don't delete the heap, because it's not
1218
+ // safe to call that function from a different thread.
1219
+ // FIXME(sgross): the interpeter check isn't great. Threads that
1220
+ // are only in subinterpreters will leak. It's trying to avoid
1221
+ // the problem where the main heap gets reset after a sub-interpreter
1222
+ // that shares the main thread gets destroyed. That will set pages_free_direct
1223
+ // to NULL and break mi_malloc calls.
1224
+ mi_thread_done ();
1225
+ if (tstate -> heap_backing ) {
1226
+ _mi_heap_done (tstate -> heap_backing );
1227
+ }
1228
+ }
1229
+
1230
+ tstate -> heap_backing = NULL ;
1231
+ tstate -> heap_obj = NULL ;
1232
+ tstate -> heap_gc = NULL ;
1207
1233
1234
+ _PyEventRC * join_event ;
1208
1235
HEAD_LOCK (runtime );
1209
1236
1210
1237
/* Unlink thread state */
@@ -1217,15 +1244,16 @@ tstate_delete_common(PyThreadState *tstate,
1217
1244
1218
1245
interp -> num_threads -- ;
1219
1246
1220
- /* The _PyEvent_Notify calls may block in acquiring parking lot locks.
1221
- * Set the status to detached so that it doesn't try to detach/reattach.
1222
- */
1223
- tstate -> status = _Py_THREAD_DETACHED ;
1247
+ // Set the status to detached so that _PyEvent_Notify does not try to
1248
+ // detach/reattach when acquiring parking lot locks.
1249
+ int32_t old_status = _Py_atomic_exchange_int32 (& tstate -> status , _Py_THREAD_DETACHED );
1224
1250
1225
1251
join_event = tstate -> join_event ;
1226
1252
tstate -> join_event = NULL ;
1227
1253
1228
- if (runtime -> stop_the_world ) {
1254
+ if (runtime -> stop_the_world &&
1255
+ !_Py_CURRENTLY_FINALIZING (runtime , tstate ) &&
1256
+ old_status != _Py_THREAD_GC ) {
1229
1257
struct _gc_runtime_state * gc = & tstate -> interp -> gc ;
1230
1258
gc -> gc_thread_countdown -- ;
1231
1259
assert (gc -> gc_thread_countdown >= 0 );
@@ -1238,63 +1266,42 @@ tstate_delete_common(PyThreadState *tstate,
1238
1266
runtime -> ref_total += tstate -> thread_ref_total ;
1239
1267
tstate -> thread_ref_total = 0 ;
1240
1268
#endif
1241
-
1242
1269
HEAD_UNLOCK (runtime );
1243
1270
1244
- /* Notify threads waiting on Thread.join(). This should happen after the
1245
- * thread state is unlinked, but must happen before tstate->os is deleted.
1246
- */
1271
+ // Notify threads waiting on Thread.join(). This should happen after the
1272
+ // thread state is unlinked, but must happen before parking lot is
1273
+ // deinitialized.
1247
1274
if (join_event ) {
1248
1275
_PyEvent_Notify (& join_event -> event );
1249
1276
_PyEventRC_Decref (join_event );
1250
1277
}
1251
1278
1252
- if (tstate -> heap_gc -> gcstate == & tstate -> interp -> gc ) {
1253
- tstate -> heap_gc -> gcstate = NULL ;
1254
- }
1255
-
1256
- if (tstate -> heap_obj -> thread_id == _Py_ThreadId () &&
1257
- _Py_IsMainInterpreter (tstate )) {
1258
- // NOTE: this may be called from a different thread. This can
1259
- // happend during shutdown of the interpreter or after forking.
1260
- // In these cases we don't delete the heap, because it's not
1261
- // safe to call that function from a different thread.
1262
- // FIXME(sgross): the interpeter check isn't great. Threads that
1263
- // are only in subinterpreters will leak. It's trying to avoid
1264
- // the problem where the main heap gets reset after a sub-interpreter
1265
- // that shares the main thread gets destroyed. That will set pages_free_direct
1266
- // to NULL and break mi_malloc calls.
1267
- mi_thread_done ();
1268
- if (tstate -> heap_backing ) {
1269
- _mi_heap_done (tstate -> heap_backing );
1270
- }
1271
- }
1272
-
1273
1279
if (is_current ) {
1274
- /* Current thread state can't be set to NULL until after join_event
1275
- * because it may be used by _PyEvent_Notify/_PyRawEvent_Notify calls .
1276
- * We also can't release it before mi_heap_done because then GC may proceed
1277
- * while abandoning is taking place. */
1280
+ // TODO(sgross): I think this should be moved up between mi_heap_done() and
1281
+ // unlinking thread state .
1282
+ // We also can't release it before mi_heap_done because then GC may proceed
1283
+ // while abandoning is taking place.
1278
1284
_PyRuntimeState_SetThreadState (runtime , NULL );
1279
1285
1280
- /* Drop the GIL if we hold it */
1286
+ // Drop the GIL if we hold it
1281
1287
PyEval_ReleaseLock ();
1282
1288
}
1283
1289
1284
- tstate -> heap_backing = NULL ;
1285
- tstate -> heap_obj = NULL ;
1286
- tstate -> heap_gc = NULL ;
1287
-
1288
- _PyParkingLot_DeinitThread (tstate -> waiter );
1289
- PyMem_RawFree (tstate );
1290
-
1291
1290
if (gilstate -> autoInterpreterState &&
1292
1291
PyThread_tss_get (& gilstate -> autoTSSkey ) == tstate )
1293
1292
{
1294
1293
PyThread_tss_set (& gilstate -> autoTSSkey , NULL );
1295
1294
}
1296
- }
1297
1295
1296
+ // TODO(sgross): what if this isn't the current thread? Might the owning
1297
+ // thread be stuck in a parking lot lock? Will it have some sort of dead
1298
+ // waiter? ruh roh.
1299
+ if (is_current ) {
1300
+ _PyParkingLot_DeinitThread (tstate -> waiter );
1301
+ }
1302
+
1303
+ PyMem_RawFree (tstate );
1304
+ }
1298
1305
1299
1306
static void
1300
1307
_PyThreadState_Delete (PyThreadState * tstate , int check_current )
0 commit comments