Skip to content

Commit 1e5713d

Browse files
fix re-entrant finalizers
1 parent 725894d commit 1e5713d

File tree

3 files changed

+28
-76
lines changed

3 files changed

+28
-76
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,10 @@ PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj);
7777
#define _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS 6
7878

7979
#ifdef _Py_TIER2
80-
PyAPI_FUNC(void) _Py_Executors_InvalidateDependencyWorldStopped(PyInterpreterState *interp, void *obj, int is_invalidation);
8180
PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
82-
PyAPI_FUNC(void) _Py_Executors_InvalidateAllWorldStopped(PyInterpreterState *interp, int is_invalidation);
8381
PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
8482
PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyThreadState *tstate);
85-
83+
PyAPI_FUNC(void) _Py_Executors_ClearExecutorList(PyObject *invalidate, int is_invalidation);
8684
#else
8785
# define _Py_Executors_InvalidateDependency(A, B, C) ((void)0)
8886
# define _Py_Executors_InvalidateAll(A, B) ((void)0)

Python/instrumentation.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,7 +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_InvalidateDependencyWorldStopped(_PyInterpreterState_GET(), code, 1);
1788+
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), code, 1);
17891789
#endif
17901790
int code_len = (int)Py_SIZE(code);
17911791
/* Exit early to avoid creating instrumentation
@@ -2028,7 +2028,7 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events)
20282028
}
20292029
set_global_version(tstate, new_version);
20302030
#ifdef _Py_TIER2
2031-
_Py_Executors_InvalidateAllWorldStopped(interp, 1);
2031+
_Py_Executors_InvalidateAll(interp, 1);
20322032
#endif
20332033
return instrument_all_executing_code_objects(interp);
20342034
}

Python/optimizer.c

Lines changed: 25 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,8 @@ _PyJit_TryInitializeTracing(
10211021
_tstate->jit_tracer_state.initial_state.jump_backward_instr = curr_instr;
10221022

10231023
if (_PyOpcode_Caches[_PyOpcode_Deopt[close_loop_instr->op.code]]) {
1024-
close_loop_instr[1].counter = trigger_backoff_counter();
1024+
_Py_BackoffCounter zero = trigger_backoff_counter();;
1025+
FT_ATOMIC_STORE_UINT16_RELAXED(close_loop_instr[1].counter.value_and_backoff, zero.value_and_backoff);
10251026
}
10261027
_Py_BloomFilter_Init(&_tstate->jit_tracer_state.prev_state.dependencies);
10271028
return 1;
@@ -1694,112 +1695,65 @@ _PyJit_Tracer_InvalidateDependency(PyThreadState *tstate, void *obj)
16941695
_tstate->jit_tracer_state.prev_state.dependencies_still_valid = false;
16951696
}
16961697
}
1698+
void
1699+
_Py_Executors_ClearExecutorList(PyObject *invalidate, int is_invalidation)
1700+
{
1701+
if (invalidate == NULL) {
1702+
return;
1703+
}
1704+
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
1705+
PyObject *exec = PyList_GET_ITEM(invalidate, i);
1706+
executor_clear(exec);
1707+
if (is_invalidation) {
1708+
OPT_STAT_INC(executors_invalidated);
1709+
}
1710+
}
1711+
Py_DECREF(invalidate);
1712+
}
16971713

16981714
/* Invalidate all executors that depend on `obj`
16991715
* May cause other executors to be invalidated as well
1716+
* To avoid deadlocks due to stop the world, we just invalidate the executors but leave them to be freed
1717+
* on their own later.
17001718
*/
17011719
void
1702-
_Py_Executors_InvalidateDependencyWorldStopped(PyInterpreterState *interp, void *obj, int is_invalidation)
1720+
_Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation)
17031721
{
17041722
_PyBloomFilter obj_filter;
17051723
_Py_BloomFilter_Init(&obj_filter);
17061724
_Py_BloomFilter_Add(&obj_filter, obj);
17071725
/* Walk the list of executors */
1708-
/* TO DO -- Use a tree to avoid traversing as many objects */
1709-
PyObject *invalidate = PyList_New(0);
1710-
if (invalidate == NULL) {
1711-
goto error;
1712-
}
17131726
/* Clearing an executor can deallocate others, so we need to make a list of
17141727
* executors to invalidate first */
17151728
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
17161729
_PyJit_Tracer_InvalidateDependency(p, obj);
17171730
for (_PyExecutorObject *exec = ((_PyThreadStateImpl *)p)->jit_executor_state.executor_list_head; exec != NULL;) {
17181731
assert(exec->vm_data.valid);
17191732
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-
}
1733+
exec->vm_data.valid = 0;
17261734
}
17271735
_PyExecutorObject *next = exec->vm_data.links.next;
17281736
exec = next;
17291737
}
17301738
}
1731-
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
1732-
PyObject *exec = PyList_GET_ITEM(invalidate, i);
1733-
executor_clear(exec);
1734-
if (is_invalidation) {
1735-
OPT_STAT_INC(executors_invalidated);
1736-
}
1737-
}
1738-
Py_DECREF(invalidate);
1739-
return;
1740-
error:
1741-
PyErr_Clear();
1742-
Py_XDECREF(invalidate);
1743-
// If we're truly out of memory, DO NOT wipe everything as the world is stopped, and we might deadlock.
1744-
}
1745-
1746-
void
1747-
_Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation)
1748-
{
1749-
_PyEval_StopTheWorld(interp);
1750-
_Py_Executors_InvalidateDependencyWorldStopped(interp, obj, is_invalidation);
1751-
_PyEval_StartTheWorld(interp);
17521739
}
17531740

1741+
// To avoid deadlocks due to stop the world, we just invalidate the executors but leave them to be freed
1742+
// on their own later.
17541743
void
1755-
_Py_Executors_InvalidateAllWorldStopped(PyInterpreterState *interp, int is_invalidation)
1744+
_Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
17561745
{
1757-
PyObject *invalidate = PyList_New(0);
1758-
if (invalidate == NULL) {
1759-
PyErr_Clear();
1760-
return;
1761-
}
17621746
/* Clearing an executor can deallocate others, so we need to make a list of
17631747
* executors to invalidate first */
17641748
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
17651749
for (_PyExecutorObject *exec = ((_PyThreadStateImpl *)p)->jit_executor_state.executor_list_head; exec != NULL;) {
17661750
assert(exec->vm_data.valid);
17671751
assert(exec->tstate == p);
1752+
exec->vm_data.valid = 0;
17681753
_PyExecutorObject *next = exec->vm_data.links.next;
1769-
if (PyList_Append(invalidate, (PyObject *)exec) < 0) {
1770-
PyErr_Clear();
1771-
Py_DECREF(invalidate);
1772-
_PyEval_StartTheWorldAll(&_PyRuntime);
1773-
return;
1774-
}
17751754
exec = next;
17761755
}
17771756
}
1778-
Py_ssize_t list_len = PyList_GET_SIZE(invalidate);
1779-
for (Py_ssize_t i = 0; i < list_len; i++) {
1780-
_PyExecutorObject *executor = (_PyExecutorObject *)PyList_GET_ITEM(invalidate, i);
1781-
1782-
assert(executor->vm_data.valid == 1);
1783-
if (executor->vm_data.code) {
1784-
// Clear the entire code object so its co_executors array be freed:
1785-
_PyCode_Clear_Executors(executor->vm_data.code);
1786-
}
1787-
executor_clear((PyObject *)executor);
1788-
if (is_invalidation) {
1789-
OPT_STAT_INC(executors_invalidated);
1790-
}
1791-
}
1792-
Py_DECREF(invalidate);
1793-
// If we're truly out of memory, DO NOT wipe everything as the world is stopped, and we might deadlock.
1794-
}
1795-
1796-
/* Invalidate all executors */
1797-
void
1798-
_Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
1799-
{
1800-
_PyEval_StopTheWorld(interp);
1801-
_Py_Executors_InvalidateAllWorldStopped(interp, is_invalidation);
1802-
_PyEval_StartTheWorld(interp);
18031757
}
18041758

18051759

0 commit comments

Comments
 (0)