https://github.com/python/cpython/commit/95e5d596308620acbd860ec25a40ef95c2b62eaa
commit: 95e5d596308620acbd860ec25a40ef95c2b62eaa
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-10-24T20:02:17+05:30
summary:
gh-140414: add fastpath for current running loop in `asyncio.all_tasks`
(#140542)
Optimize `asyncio.all_tasks()` for the common case where the event loop is
running in the current thread by avoiding stop-the-world pauses and locking.
This optimization is already present for `asyncio.current_task()` so we do the
same for `asyncio.all_tasks()`.
files:
M Modules/_asynciomodule.c
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 99408e60721c60..1f58b1fb3506c6 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -4079,30 +4079,44 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject
*loop)
return NULL;
}
- PyInterpreterState *interp = PyInterpreterState_Get();
- // Stop the world and traverse the per-thread linked list
- // of asyncio tasks for every thread, as well as the
- // interpreter's linked list, and add them to `tasks`.
- // The interpreter linked list is used for any lingering tasks
- // whose thread state has been deallocated while the task was
- // still alive. This can happen if a task is referenced by
- // a different thread, in which case the task is moved to
- // the interpreter's linked list from the thread's linked
- // list before deallocation. See PyThreadState_Clear.
- //
- // The stop-the-world pause is required so that no thread
- // modifies its linked list while being iterated here
- // in parallel. This design allows for lock-free
- // register_task/unregister_task for loops running in parallel
- // in different threads (the general case).
- _PyEval_StopTheWorld(interp);
- int ret = add_tasks_interp(interp, (PyListObject *)tasks);
- _PyEval_StartTheWorld(interp);
- if (ret < 0) {
- // call any escaping calls after starting the world to avoid any
deadlocks.
- Py_DECREF(tasks);
- Py_DECREF(loop);
- return NULL;
+ _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET();
+ if (ts->asyncio_running_loop == loop) {
+ // Fast path for the current running loop of current thread
+ // no locking or stop the world pause is required
+ struct llist_node *head = &ts->asyncio_tasks_head;
+ if (add_tasks_llist(head, (PyListObject *)tasks) < 0) {
+ Py_DECREF(tasks);
+ Py_DECREF(loop);
+ return NULL;
+ }
+ }
+ else {
+ // Slow path for loop running in different thread
+ PyInterpreterState *interp = ts->base.interp;
+ // Stop the world and traverse the per-thread linked list
+ // of asyncio tasks for every thread, as well as the
+ // interpreter's linked list, and add them to `tasks`.
+ // The interpreter linked list is used for any lingering tasks
+ // whose thread state has been deallocated while the task was
+ // still alive. This can happen if a task is referenced by
+ // a different thread, in which case the task is moved to
+ // the interpreter's linked list from the thread's linked
+ // list before deallocation. See PyThreadState_Clear.
+ //
+ // The stop-the-world pause is required so that no thread
+ // modifies its linked list while being iterated here
+ // in parallel. This design allows for lock-free
+ // register_task/unregister_task for loops running in parallel
+ // in different threads (the general case).
+ _PyEval_StopTheWorld(interp);
+ int ret = add_tasks_interp(interp, (PyListObject *)tasks);
+ _PyEval_StartTheWorld(interp);
+ if (ret < 0) {
+ // call any escaping calls after starting the world to avoid any
deadlocks.
+ Py_DECREF(tasks);
+ Py_DECREF(loop);
+ return NULL;
+ }
}
// All the tasks are now in the list, now filter the tasks which are done
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]