@@ -1808,13 +1808,6 @@ PyThreadState_Clear(PyThreadState *tstate)
18081808 Py_CLEAR (((_PyThreadStateImpl * )tstate )-> asyncio_running_task );
18091809
18101810
1811- PyMutex_Lock (& tstate -> interp -> asyncio_tasks_lock );
1812- // merge any lingering tasks from thread state to interpreter's
1813- // tasks list
1814- llist_concat (& tstate -> interp -> asyncio_tasks_head ,
1815- & ((_PyThreadStateImpl * )tstate )-> asyncio_tasks_head );
1816- PyMutex_Unlock (& tstate -> interp -> asyncio_tasks_lock );
1817-
18181811 Py_CLEAR (tstate -> dict );
18191812 Py_CLEAR (tstate -> async_exc );
18201813
@@ -1890,11 +1883,30 @@ PyThreadState_Clear(PyThreadState *tstate)
18901883static void
18911884decrement_stoptheworld_countdown (struct _stoptheworld_state * stw );
18921885
1893- /* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */
1886+ // `release_gil` releases the current thread's GIL. For states already removed
1887+ // by _PyThreadState_RemoveExcept(), `is_unlinked` limits cleanup to task transfer.
18941888static void
1895- tstate_delete_common (PyThreadState * tstate , int release_gil )
1889+ tstate_delete_common (PyThreadState * tstate , int release_gil , int is_unlinked )
18961890{
18971891 assert (tstate -> _status .cleared && !tstate -> _status .finalized );
1892+ assert (!is_unlinked || !release_gil );
1893+
1894+ _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )tstate ;
1895+ // Finalizers may register tasks during or after PyThreadState_Clear().
1896+ // Transfer them before normal unlinking or GIL release: cross-thread task
1897+ // removal uses stop-the-world or the GIL, not asyncio_tasks_lock.
1898+ if (!llist_empty (& tstate_impl -> asyncio_tasks_head )) {
1899+ // Serialize transfers from concurrently exiting threads.
1900+ PyMutex_Lock (& tstate -> interp -> asyncio_tasks_lock );
1901+ llist_concat (& tstate -> interp -> asyncio_tasks_head ,
1902+ & tstate_impl -> asyncio_tasks_head );
1903+ PyMutex_Unlock (& tstate -> interp -> asyncio_tasks_lock );
1904+ }
1905+ assert (llist_empty (& tstate_impl -> asyncio_tasks_head ));
1906+ if (is_unlinked ) {
1907+ return ;
1908+ }
1909+
18981910 tstate_verify_not_active (tstate );
18991911 assert (!_PyThreadState_IsRunningMain (tstate ));
19001912
@@ -1930,7 +1942,6 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
19301942
19311943#if defined(Py_REF_DEBUG ) && defined(Py_GIL_DISABLED )
19321944 // Add our portion of the total refcount to the interpreter's total.
1933- _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )tstate ;
19341945 tstate -> interp -> object_state .reftotal += tstate_impl -> reftotal ;
19351946 tstate_impl -> reftotal = 0 ;
19361947 assert (tstate_impl -> refcounts .values == NULL );
@@ -1977,7 +1988,7 @@ zapthreads(PyInterpreterState *interp)
19771988 */
19781989 while ((tstate = interp -> threads .head ) != NULL ) {
19791990 tstate_verify_not_active (tstate );
1980- tstate_delete_common (tstate , 0 );
1991+ tstate_delete_common (tstate , /*release_gil=*/ 0 , /*is_unlinked=*/ 0 );
19811992 free_threadstate ((_PyThreadStateImpl * )tstate );
19821993 }
19831994}
@@ -1988,7 +1999,7 @@ PyThreadState_Delete(PyThreadState *tstate)
19881999{
19892000 _Py_EnsureTstateNotNULL (tstate );
19902001 tstate_verify_not_active (tstate );
1991- tstate_delete_common (tstate , 0 );
2002+ tstate_delete_common (tstate , /*release_gil=*/ 0 , /*is_unlinked=*/ 0 );
19922003 free_threadstate ((_PyThreadStateImpl * )tstate );
19932004}
19942005
@@ -2004,7 +2015,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate)
20042015 _PyStats_Detach ((_PyThreadStateImpl * )tstate );
20052016#endif
20062017 current_fast_clear (tstate -> interp -> runtime );
2007- tstate_delete_common (tstate , 1 ); // release GIL as part of call
2018+ tstate_delete_common (tstate , /*release_gil=*/ 1 , /*is_unlinked=*/ 0 );
20082019 free_threadstate ((_PyThreadStateImpl * )tstate );
20092020}
20102021
@@ -2074,6 +2085,7 @@ _PyThreadState_DeleteList(PyThreadState *list, int is_after_fork)
20742085 for (p = list ; p ; p = next ) {
20752086 next = p -> next ;
20762087 PyThreadState_Clear (p );
2088+ tstate_delete_common (p , /*release_gil=*/ 0 , /*is_unlinked=*/ 1 );
20772089 if (is_after_fork ) {
20782090 free_threadstate ((_PyThreadStateImpl * )p );
20792091 }
0 commit comments