Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add comments
  • Loading branch information
kumaraditya303 committed Jan 15, 2025
commit acef8212b4c92bd0c177b951360a95153c0d7454
3 changes: 3 additions & 0 deletions Include/internal/pycore_tstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ typedef struct _PyThreadStateImpl {

PyObject *asyncio_running_loop; // Strong reference

/* Head of circular linked-list of all tasks which are instances of `asyncio.Task`
or subclasses of it used in `asyncio.all_tasks`.
*/
struct llist_node asyncio_tasks_head;
struct _qsbr_thread_state *qsbr; // only used by free-threaded build
struct llist_node mem_free_queue; // delayed free queue
Expand Down
17 changes: 14 additions & 3 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3747,9 +3747,16 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
return NULL;
}
int err = 0;
struct llist_node *node;
PyInterpreterState *interp = PyInterpreterState_Get();
// Stop the world and traverse the per-thread linked list
// of asyncio tasks of all threads and add them to the list.
// Stop the world pause is required so that no thread
// modifies it's linked list while being iterated here
// concurrently.
// This design allows for lock free register/unregister of tasks
// of loops running concurrently in different threads.
_PyEval_StopTheWorld(interp);
struct llist_node *node;
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;
struct llist_node *head = &tstate->asyncio_tasks_head;
Expand All @@ -3764,8 +3771,10 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
// otherwise it gets added to the list.
if (_Py_TryIncref((PyObject *)task)) {
if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) {
Py_DECREF(tasks);
Py_DECREF(loop);
// do not call any escaping function such as Py_DECREF
// while holding the runtime lock, instead set err=1 and
// call them after releasing the runtime lock
// and starting the world to avoid any deadlocks.
err = 1;
break;
}
Expand All @@ -3775,6 +3784,8 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
_Py_FOR_EACH_TSTATE_END(interp);
_PyEval_StartTheWorld(interp);
if (err) {
Py_DECREF(tasks);
Py_DECREF(loop);
return NULL;
}
PyObject *scheduled_iter = PyObject_GetIter(state->non_asyncio_tasks);
Expand Down
2 changes: 2 additions & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,8 @@ PyThreadState_Clear(PyThreadState *tstate)
Py_CLEAR(((_PyThreadStateImpl *)tstate)->asyncio_running_loop);

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