@@ -1797,8 +1797,8 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
17971797// Unlike _PyExecutor_InvalidateDependency, this is not for correctness but memory savings.
17981798// Thus there is no need to lock the runtime or traverse everything. We simply make a
17991799// best-effort attempt to clean things up.
1800- void
1801- _Py_Executors_InvalidateCold (PyThreadState * tstate )
1800+ PyObject *
1801+ _Py_Executors_CollectCold (PyThreadState * tstate )
18021802{
18031803 /* Walk the list of executors */
18041804 /* TO DO -- Use a tree to avoid traversing as many objects */
@@ -1813,8 +1813,8 @@ _Py_Executors_InvalidateCold(PyThreadState *tstate)
18131813 _PyExecutorObject * next = exec -> vm_data .links .next ;
18141814
18151815 if (!exec -> vm_data .warm || !exec -> vm_data .valid ) {
1816+ FT_ATOMIC_STORE_UINT8 (exec -> vm_data .valid , 0 );
18161817 if (PyList_Append (invalidate , (PyObject * )exec ) < 0 ) {
1817- FT_ATOMIC_STORE_UINT8 (exec -> vm_data .valid , 0 );
18181818 goto error ;
18191819 }
18201820 }
@@ -1824,19 +1824,70 @@ _Py_Executors_InvalidateCold(PyThreadState *tstate)
18241824
18251825 exec = next ;
18261826 }
1827+ return invalidate ;
1828+ error :
1829+ PyErr_Clear ();
1830+ Py_XDECREF (invalidate );
1831+ return NULL ;
1832+ }
1833+
1834+ void
1835+ _Py_Executors_ClearColdList (PyObject * invalidate )
1836+ {
1837+ if (invalidate == NULL ) {
1838+ return ;
1839+ }
18271840 for (Py_ssize_t i = 0 ; i < PyList_GET_SIZE (invalidate ); i ++ ) {
18281841 PyObject * exec = PyList_GET_ITEM (invalidate , i );
18291842 executor_clear (exec );
18301843 }
18311844 Py_DECREF (invalidate );
1845+ }
1846+
1847+ void
1848+ _Py_Executors_InvalidateCold (PyThreadState * tstate )
1849+ {
1850+ _Py_Executors_ClearColdList (_Py_Executors_CollectCold (tstate ));
1851+ }
1852+
1853+ void
1854+ _Py_Executors_InvalidateColdGC (PyInterpreterState * interp )
1855+ {
1856+ PyObject * invalidate = PyList_New (0 );
1857+ if (invalidate == NULL ) {
1858+ PyErr_Clear ();
1859+ return ;
1860+ }
1861+ _PyEval_StopTheWorld (interp );
1862+ HEAD_LOCK (interp -> runtime );
1863+ _Py_FOR_EACH_TSTATE_UNLOCKED (interp , p )
1864+ {
1865+ PyObject * more_invalidate = _Py_Executors_CollectCold (p );
1866+ if (more_invalidate == NULL ) {
1867+ goto error ;
1868+ }
1869+ int err = PyList_Extend (invalidate , more_invalidate );
1870+ Py_DECREF (more_invalidate );
1871+ if (err ) {
1872+ goto error ;
1873+ }
1874+ }
1875+ HEAD_UNLOCK (interp -> runtime );
1876+ _PyEval_StartTheWorld (interp );
1877+ // We can only clear the list after unlocking the runtime.
1878+ // Otherwise, it may deadlock.
1879+ _Py_Executors_ClearColdList (invalidate );
18321880 return ;
18331881error :
18341882 PyErr_Clear ();
1835- Py_XDECREF (invalidate );
1836- // If we're truly out of memory, wiping out everything is a fine fallback
1837- _Py_Executors_InvalidateAll (_PyInterpreterState_GET (), 0 );
1883+ Py_DECREF (invalidate );
1884+ // Invalidate all the executors if we run out of memory.
1885+ // This means the next run will remove all executors.
1886+ _Py_Executors_InvalidateAllLockHeld (interp , 0 );
1887+ HEAD_UNLOCK (interp -> runtime );
18381888}
18391889
1890+
18401891static void
18411892write_str (PyObject * str , FILE * out )
18421893{
0 commit comments