Skip to content

Commit acef821

Browse files
add comments
1 parent 3052330 commit acef821

File tree

3 files changed

+19
-3
lines changed

3 files changed

+19
-3
lines changed

Include/internal/pycore_tstate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ typedef struct _PyThreadStateImpl {
2323

2424
PyObject *asyncio_running_loop; // Strong reference
2525

26+
/* Head of circular linked-list of all tasks which are instances of `asyncio.Task`
27+
or subclasses of it used in `asyncio.all_tasks`.
28+
*/
2629
struct llist_node asyncio_tasks_head;
2730
struct _qsbr_thread_state *qsbr; // only used by free-threaded build
2831
struct llist_node mem_free_queue; // delayed free queue

Modules/_asynciomodule.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3747,9 +3747,16 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37473747
return NULL;
37483748
}
37493749
int err = 0;
3750-
struct llist_node *node;
37513750
PyInterpreterState *interp = PyInterpreterState_Get();
3751+
// Stop the world and traverse the per-thread linked list
3752+
// of asyncio tasks of all threads and add them to the list.
3753+
// Stop the world pause is required so that no thread
3754+
// modifies it's linked list while being iterated here
3755+
// concurrently.
3756+
// This design allows for lock free register/unregister of tasks
3757+
// of loops running concurrently in different threads.
37523758
_PyEval_StopTheWorld(interp);
3759+
struct llist_node *node;
37533760
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
37543761
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;
37553762
struct llist_node *head = &tstate->asyncio_tasks_head;
@@ -3764,8 +3771,10 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37643771
// otherwise it gets added to the list.
37653772
if (_Py_TryIncref((PyObject *)task)) {
37663773
if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) {
3767-
Py_DECREF(tasks);
3768-
Py_DECREF(loop);
3774+
// do not call any escaping function such as Py_DECREF
3775+
// while holding the runtime lock, instead set err=1 and
3776+
// call them after releasing the runtime lock
3777+
// and starting the world to avoid any deadlocks.
37693778
err = 1;
37703779
break;
37713780
}
@@ -3775,6 +3784,8 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37753784
_Py_FOR_EACH_TSTATE_END(interp);
37763785
_PyEval_StartTheWorld(interp);
37773786
if (err) {
3787+
Py_DECREF(tasks);
3788+
Py_DECREF(loop);
37783789
return NULL;
37793790
}
37803791
PyObject *scheduled_iter = PyObject_GetIter(state->non_asyncio_tasks);

Python/pystate.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,8 @@ PyThreadState_Clear(PyThreadState *tstate)
16991699
Py_CLEAR(((_PyThreadStateImpl *)tstate)->asyncio_running_loop);
17001700

17011701
struct llist_node *node;
1702+
// Clear any lingering tasks so that `TaskObj_finalize` doesn't
1703+
// try to unregister task from a freed list.
17021704
llist_for_each_safe(node, &((_PyThreadStateImpl *)tstate)->asyncio_tasks_head) {
17031705
llist_remove(node);
17041706
}

0 commit comments

Comments
 (0)