mirror of
https://github.com/python/cpython.git
synced 2026-04-14 15:50:50 +00:00
[3.14] gh-140414: add fastpath for current running loop in asyncio.all_tasks (GH-140542) (#144494)
* gh-140414: add fastpath for current running loop in `asyncio.all_tasks` (GH-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()`.
(cherry picked from commit 95e5d59630)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
This commit is contained in:
parent
6614a3c30c
commit
f4239df276
2 changed files with 40 additions and 24 deletions
|
|
@ -0,0 +1,2 @@
|
|||
Fix performance regression in :func:`asyncio.all_tasks` on
|
||||
:term:`free-threaded builds <free-threaded build>`. Patch by Kumar Aditya.
|
||||
|
|
@ -4075,30 +4075,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue