@@ -1172,6 +1172,7 @@ allocate_executor(int exit_count, int length)
11721172 res -> trace = (_PyUOpInstruction * )(res -> exits + exit_count );
11731173 res -> code_size = length ;
11741174 res -> exit_count = exit_count ;
1175+ res -> tstate = _PyThreadState_GET ();
11751176 return res ;
11761177}
11771178
@@ -1534,7 +1535,7 @@ unlink_executor(_PyExecutorObject *executor)
15341535 }
15351536 else {
15361537 // prev == NULL implies that executor is the list head
1537- _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )_PyThreadState_GET () ;
1538+ _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )executor -> tstate ;
15381539 assert (_tstate -> jit_executor_state .executor_list_head == executor );
15391540 _tstate -> jit_executor_state .executor_list_head = next ;
15401541 }
@@ -1680,6 +1681,19 @@ _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj)
16801681 _Py_BloomFilter_Add (& executor -> vm_data .bloom , obj );
16811682}
16821683
1684+ void
1685+ _PyJit_Tracer_InvalidateDependency (PyThreadState * tstate , void * obj )
1686+ {
1687+ _PyBloomFilter obj_filter ;
1688+ _Py_BloomFilter_Init (& obj_filter );
1689+ _Py_BloomFilter_Add (& obj_filter , obj );
1690+ _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )tstate ;
1691+ if (bloom_filter_may_contain (& _tstate -> jit_tracer_state .prev_state .dependencies , & obj_filter ))
1692+ {
1693+ _tstate -> jit_tracer_state .prev_state .dependencies_still_valid = false;
1694+ }
1695+ }
1696+
16831697/* Invalidate all executors that depend on `obj`
16841698 * May cause other executors to be invalidated as well
16851699 */
@@ -1695,19 +1709,26 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is
16951709 if (invalidate == NULL ) {
16961710 goto error ;
16971711 }
1698- _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )_PyThreadState_GET ();
16991712 /* Clearing an executor can deallocate others, so we need to make a list of
17001713 * executors to invalidate first */
1701- for (_PyExecutorObject * exec = _tstate -> jit_executor_state .executor_list_head ; exec != NULL ;) {
1702- assert (exec -> vm_data .valid );
1703- _PyExecutorObject * next = exec -> vm_data .links .next ;
1704- if (bloom_filter_may_contain (& exec -> vm_data .bloom , & obj_filter ) &&
1705- PyList_Append (invalidate , (PyObject * )exec ))
1706- {
1707- goto error ;
1714+ _PyEval_StopTheWorldAll (& _PyRuntime );
1715+ _Py_FOR_EACH_TSTATE_UNLOCKED (interp , p ) {
1716+ _PyJit_Tracer_InvalidateDependency (p , obj );
1717+ for (_PyExecutorObject * exec = ((_PyThreadStateImpl * )p )-> jit_executor_state .executor_list_head ; exec != NULL ;) {
1718+ assert (exec -> vm_data .valid );
1719+ if (bloom_filter_may_contain (& exec -> vm_data .bloom , & obj_filter )) {
1720+ if (PyList_Append (invalidate , (PyObject * )exec ) < 0 ) {
1721+ PyErr_Clear ();
1722+ Py_DECREF (invalidate );
1723+ _PyEval_StartTheWorldAll (& _PyRuntime );
1724+ return ;
1725+ }
1726+ }
1727+ _PyExecutorObject * next = exec -> vm_data .links .next ;
1728+ exec = next ;
17081729 }
1709- exec = next ;
17101730 }
1731+ _PyEval_StartTheWorldAll (& _PyRuntime );
17111732 for (Py_ssize_t i = 0 ; i < PyList_GET_SIZE (invalidate ); i ++ ) {
17121733 PyObject * exec = PyList_GET_ITEM (invalidate , i );
17131734 executor_clear (exec );
@@ -1724,49 +1745,64 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is
17241745 _Py_Executors_InvalidateAll (interp , is_invalidation );
17251746}
17261747
1727- void
1728- _PyJit_Tracer_InvalidateDependency (PyThreadState * tstate , void * obj )
1729- {
1730- _PyBloomFilter obj_filter ;
1731- _Py_BloomFilter_Init (& obj_filter );
1732- _Py_BloomFilter_Add (& obj_filter , obj );
1733- _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )tstate ;
1734- if (bloom_filter_may_contain (& _tstate -> jit_tracer_state .prev_state .dependencies , & obj_filter ))
1735- {
1736- _tstate -> jit_tracer_state .prev_state .dependencies_still_valid = false;
1737- }
1738- }
17391748/* Invalidate all executors */
17401749void
17411750_Py_Executors_InvalidateAll (PyInterpreterState * interp , int is_invalidation )
17421751{
1743- _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )_PyThreadState_GET ();
1744- while (_tstate -> jit_executor_state .executor_list_head ) {
1745- _PyExecutorObject * executor = _tstate -> jit_executor_state .executor_list_head ;
1746- assert (executor -> vm_data .valid == 1 && executor -> vm_data .linked == 1 );
1752+ PyObject * invalidate = PyList_New (0 );
1753+ if (invalidate == NULL ) {
1754+ PyErr_Clear ();
1755+ return ;
1756+ }
1757+ /* Clearing an executor can deallocate others, so we need to make a list of
1758+ * executors to invalidate first */
1759+ _PyEval_StopTheWorldAll (& _PyRuntime );
1760+ _Py_FOR_EACH_TSTATE_UNLOCKED (interp , p ) {
1761+ for (_PyExecutorObject * exec = ((_PyThreadStateImpl * )p )-> jit_executor_state .executor_list_head ; exec != NULL ;) {
1762+ assert (exec -> vm_data .valid );
1763+ assert (exec -> tstate == p );
1764+ _PyExecutorObject * next = exec -> vm_data .links .next ;
1765+ if (PyList_Append (invalidate , (PyObject * )exec ) < 0 ) {
1766+ PyErr_Clear ();
1767+ Py_DECREF (invalidate );
1768+ _PyEval_StartTheWorldAll (& _PyRuntime );
1769+ return ;
1770+ }
1771+ exec = next ;
1772+ }
1773+ }
1774+ _PyEval_StartTheWorldAll (& _PyRuntime );
1775+ Py_ssize_t list_len = PyList_GET_SIZE (invalidate );
1776+ for (Py_ssize_t i = 0 ; i < list_len ; i ++ ) {
1777+ _PyExecutorObject * executor = (_PyExecutorObject * )PyList_GET_ITEM (invalidate , i );
1778+
1779+ assert (executor -> vm_data .valid == 1 );
17471780 if (executor -> vm_data .code ) {
17481781 // Clear the entire code object so its co_executors array be freed:
17491782 _PyCode_Clear_Executors (executor -> vm_data .code );
17501783 }
1751- else {
1752- executor_clear ((PyObject * )executor );
1753- }
1784+ executor_clear ((PyObject * )executor );
17541785 if (is_invalidation ) {
17551786 OPT_STAT_INC (executors_invalidated );
17561787 }
17571788 }
1789+ Py_DECREF (invalidate );
17581790}
17591791
1792+
1793+ // Unlike _PyExecutor_InvalidateDependency, this is not for correctness but memory savings.
1794+ // Thus there is no need to lock the runtime or traverse everything. We simply make a
1795+ // best-effort attempt to clean things up.
17601796void
1761- _Py_Executors_InvalidateCold (PyInterpreterState * interp )
1797+ _Py_Executors_InvalidateCold (PyThreadState * tstate )
17621798{
17631799 /* Walk the list of executors */
17641800 /* TO DO -- Use a tree to avoid traversing as many objects */
17651801 PyObject * invalidate = PyList_New (0 );
17661802 if (invalidate == NULL ) {
17671803 goto error ;
17681804 }
1769- _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )_PyThreadState_GET () ;
1805+ _PyThreadStateImpl * _tstate = (_PyThreadStateImpl * )tstate ;
17701806 /* Clearing an executor can deallocate others, so we need to make a list of
17711807 * executors to invalidate first */
17721808 for (_PyExecutorObject * exec = _tstate -> jit_executor_state .executor_list_head ; exec != NULL ;) {
@@ -1792,7 +1828,7 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp)
17921828 PyErr_Clear ();
17931829 Py_XDECREF (invalidate );
17941830 // If we're truly out of memory, wiping out everything is a fine fallback
1795- _Py_Executors_InvalidateAll (interp , 0 );
1831+ _Py_Executors_InvalidateAll (_PyInterpreterState_GET () , 0 );
17961832}
17971833
17981834static void
0 commit comments