Skip to content

Commit ba9a65a

Browse files
fix a bug with traversing states
1 parent 6a3bd75 commit ba9a65a

File tree

9 files changed

+75
-42
lines changed

9 files changed

+75
-42
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct _PyExitData {
4949

5050
typedef struct _PyExecutorObject {
5151
PyObject_VAR_HEAD
52+
PyThreadState *tstate;
5253
const _PyUOpInstruction *trace;
5354
_PyVMData vm_data; /* Used by the VM, but opaque to the optimizer */
5455
uint32_t exit_count;
@@ -78,7 +79,7 @@ PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj);
7879
#ifdef _Py_TIER2
7980
PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
8081
PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
81-
PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);
82+
PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyThreadState *tstate);
8283

8384
#else
8485
# define _Py_Executors_InvalidateDependency(A, B, C) ((void)0)

Modules/_testinternalcapi.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,8 +1240,7 @@ add_executor_dependency(PyObject *self, PyObject *args)
12401240
static PyObject *
12411241
invalidate_executors(PyObject *self, PyObject *obj)
12421242
{
1243-
PyInterpreterState *interp = PyInterpreterState_Get();
1244-
_Py_Executors_InvalidateDependency(interp, obj, 1);
1243+
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), obj, 1);
12451244
Py_RETURN_NONE;
12461245
}
12471246

Objects/codeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2432,7 +2432,7 @@ code_dealloc(PyObject *self)
24322432
PyMem_Free(co_extra);
24332433
}
24342434
#ifdef _Py_TIER2
2435-
_PyJit_Tracer_InvalidateDependency(tstate, self);
2435+
_Py_Executors_InvalidateDependency(tstate->interp, self, 1);
24362436
if (co->co_executors != NULL) {
24372437
clear_executors(co);
24382438
}

Objects/frameobject.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value)
263263

264264
#if _Py_TIER2
265265
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), co, 1);
266-
_PyJit_Tracer_InvalidateDependency(_PyThreadState_GET(), co);
267266
#endif
268267

269268
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);

Objects/funcobject.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,6 @@ func_dealloc(PyObject *self)
11531153
}
11541154
#if _Py_TIER2
11551155
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), self, 1);
1156-
_PyJit_Tracer_InvalidateDependency(_PyThreadState_GET(), self);
11571156
#endif
11581157
_PyObject_GC_UNTRACK(op);
11591158
FT_CLEAR_WEAKREFS(self, op->func_weakreflist);

Python/ceval_gil.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,7 @@ _Py_HandlePending(PyThreadState *tstate)
13971397

13981398
if ((breaker & _PY_EVAL_JIT_INVALIDATE_COLD_BIT) != 0) {
13991399
_Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
1400-
_Py_Executors_InvalidateCold(tstate->interp);
1400+
_Py_Executors_InvalidateCold(tstate);
14011401
((_PyThreadStateImpl*)tstate)->jit_executor_state.executor_creation_counter = JIT_CLEANUP_THRESHOLD;
14021402
}
14031403

Python/instrumentation.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,8 +1785,7 @@ force_instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
17851785
if (code->co_executors != NULL) {
17861786
_PyCode_Clear_Executors(code);
17871787
}
1788-
_Py_Executors_InvalidateDependency(interp, code, 1);
1789-
_PyJit_Tracer_InvalidateDependency(PyThreadState_GET(), code);
1788+
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), code, 1);
17901789
#endif
17911790
int code_len = (int)Py_SIZE(code);
17921791
/* Exit early to avoid creating instrumentation

Python/optimizer.c

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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 */
17401749
void
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.
17601796
void
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

17981834
static void

Python/pylifecycle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, Py
606606
PyInterpreterState *interp = _PyInterpreterState_GET();
607607
#ifdef _Py_TIER2
608608
if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
609-
_Py_Executors_InvalidateAll(interp, 1);
609+
_Py_Executors_InvalidateAll(_PyInterpreterState_GET(), 1);
610610
}
611611
#endif
612612
RARE_EVENT_INTERP_INC(interp, builtin_dict);

0 commit comments

Comments
 (0)