@@ -1203,8 +1203,35 @@ tstate_delete_common(PyThreadState *tstate,
12031203 Py_FatalError ("PyThreadState_Delete: NULL interp" );
12041204 }
12051205
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 ;
12071233
1234+ _PyEventRC * join_event ;
12081235 HEAD_LOCK (runtime );
12091236
12101237 /* Unlink thread state */
@@ -1217,15 +1244,16 @@ tstate_delete_common(PyThreadState *tstate,
12171244
12181245 interp -> num_threads -- ;
12191246
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 );
12241250
12251251 join_event = tstate -> join_event ;
12261252 tstate -> join_event = NULL ;
12271253
1228- if (runtime -> stop_the_world ) {
1254+ if (runtime -> stop_the_world &&
1255+ !_Py_CURRENTLY_FINALIZING (runtime , tstate ) &&
1256+ old_status != _Py_THREAD_GC ) {
12291257 struct _gc_runtime_state * gc = & tstate -> interp -> gc ;
12301258 gc -> gc_thread_countdown -- ;
12311259 assert (gc -> gc_thread_countdown >= 0 );
@@ -1238,63 +1266,42 @@ tstate_delete_common(PyThreadState *tstate,
12381266 runtime -> ref_total += tstate -> thread_ref_total ;
12391267 tstate -> thread_ref_total = 0 ;
12401268#endif
1241-
12421269 HEAD_UNLOCK (runtime );
12431270
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.
12471274 if (join_event ) {
12481275 _PyEvent_Notify (& join_event -> event );
12491276 _PyEventRC_Decref (join_event );
12501277 }
12511278
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-
12731279 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.
12781284 _PyRuntimeState_SetThreadState (runtime , NULL );
12791285
1280- /* Drop the GIL if we hold it */
1286+ // Drop the GIL if we hold it
12811287 PyEval_ReleaseLock ();
12821288 }
12831289
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-
12911290 if (gilstate -> autoInterpreterState &&
12921291 PyThread_tss_get (& gilstate -> autoTSSkey ) == tstate )
12931292 {
12941293 PyThread_tss_set (& gilstate -> autoTSSkey , NULL );
12951294 }
1296- }
12971295
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+ }
12981305
12991306static void
13001307_PyThreadState_Delete (PyThreadState * tstate , int check_current )
0 commit comments