Skip to content

Commit 6858161

Browse files
committed
Free the executors outside the lock in _Py_ClearExecutorDeletionList
1 parent 283f453 commit 6858161

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

Python/optimizer.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ _Py_ClearExecutorDeletionList(PyInterpreterState *interp)
245245
ts = PyThreadState_Next(ts);
246246
HEAD_UNLOCK(runtime);
247247
}
248+
249+
/* Create a list to collect executors that need to be freed.
250+
* This avoids calling _PyExecutor_Free while holding the lock,
251+
* which could trigger destructors and cause deadlock. */
252+
PyObject *to_free = PyList_New(0);
253+
if (to_free == NULL) {
254+
goto error;
255+
}
256+
248257
EXECUTOR_LIST_LOCK(interp);
249258
_PyExecutorObject **prev_to_next_ptr = &interp->executor_deletion_list_head;
250259
_PyExecutorObject *exec = *prev_to_next_ptr;
@@ -256,12 +265,26 @@ _Py_ClearExecutorDeletionList(PyInterpreterState *interp)
256265
}
257266
else {
258267
*prev_to_next_ptr = exec->vm_data.links.next;
259-
_PyExecutor_Free(exec);
268+
if (PyList_Append(to_free, (PyObject *)exec)) {
269+
EXECUTOR_LIST_UNLOCK(interp);
270+
goto error;
271+
}
260272
}
261273
exec = *prev_to_next_ptr;
262274
}
263275
interp->executor_deletion_list_remaining_capacity = EXECUTOR_DELETE_LIST_MAX;
264276
EXECUTOR_LIST_UNLOCK(interp);
277+
278+
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(to_free); i++) {
279+
_PyExecutorObject *exec = (_PyExecutorObject *)PyList_GET_ITEM(to_free, i);
280+
_PyExecutor_Free(exec);
281+
}
282+
Py_DECREF(to_free);
283+
return;
284+
285+
error:
286+
PyErr_Clear();
287+
Py_XDECREF(to_free);
265288
}
266289

267290
static void

0 commit comments

Comments
 (0)