Skip to content

Commit 7dc41ad

Browse files
gh-128002: fix asyncio.all_tasks against concurrent deallocations of tasks (#128541)
1 parent b725297 commit 7dc41ad

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed

Include/internal/pycore_object.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
120120
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
121121

122122
extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t);
123-
extern void _Py_IncRefTotal(PyThreadState *);
123+
extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *);
124124
extern void _Py_DecRefTotal(PyThreadState *);
125125

126126
# define _Py_DEC_REFTOTAL(interp) \

Modules/_asynciomodule.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -3772,11 +3772,20 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37723772

37733773
llist_for_each_safe(node, &state->asyncio_tasks_head) {
37743774
TaskObj *task = llist_data(node, TaskObj, task_node);
3775-
if (PyList_Append(tasks, (PyObject *)task) < 0) {
3776-
Py_DECREF(tasks);
3777-
Py_DECREF(loop);
3778-
err = 1;
3779-
break;
3775+
// The linked list holds borrowed references to task
3776+
// as such it is possible that the task is concurrently
3777+
// deallocated while added to this list.
3778+
// To protect against concurrent deallocations,
3779+
// we first try to incref the task which would fail
3780+
// if it is concurrently getting deallocated in another thread,
3781+
// otherwise it gets added to the list.
3782+
if (_Py_TryIncref((PyObject *)task)) {
3783+
if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) {
3784+
Py_DECREF(tasks);
3785+
Py_DECREF(loop);
3786+
err = 1;
3787+
break;
3788+
}
37803789
}
37813790
}
37823791
ASYNCIO_STATE_UNLOCK(state);

0 commit comments

Comments
 (0)