gh-128002: use internal llist implementation for asyncio tasks (#128256)

This commit is contained in:
Kumar Aditya 2024-12-25 17:51:27 +05:30 committed by GitHub
parent d9ed42bc00
commit 76f1785657
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,9 +6,9 @@
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_MUT()
#include "pycore_dict.h" // _PyDict_GetItem_KnownHash()
#include "pycore_freelist.h" // _Py_FREELIST_POP()
#include "pycore_llist.h" // struct llist_node
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _Py_SetImmortalUntracked()
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing()
#include "pycore_pystate.h" // _PyThreadState_GET()
@ -60,8 +60,7 @@ typedef struct TaskObj {
PyObject *task_coro;
PyObject *task_name;
PyObject *task_context;
struct TaskObj *next;
struct TaskObj *prev;
struct llist_node task_node;
} TaskObj;
typedef struct {
@ -136,21 +135,11 @@ typedef struct {
/* Counter for autogenerated Task names */
uint64_t task_name_counter;
/* Circular linked-list of all tasks which are instances of asyncio.Task or subclasses
of it. Third party tasks implementations which don't inherit from
asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet.
`first` is used as a sentinel to mark the end of the linked-list. It avoids one
branch in checking for empty list when adding a new task, the list is
initialized with `head`, `head->next` and `head->prev` pointing to `first`
to mark an empty list.
/* Head of circular linked-list of all tasks which are instances of `asyncio.Task`
or subclasses of it. Third party tasks implementations which don't inherit from
`asyncio.Task` are tracked separately using the `non_asyncio_tasks` WeakSet.
*/
struct {
TaskObj first;
TaskObj *head;
} asyncio_tasks;
struct llist_node asyncio_tasks_head;
} asyncio_state;
static inline asyncio_state *
@ -1896,19 +1885,12 @@ register_task(asyncio_state *state, TaskObj *task)
{
ASYNCIO_STATE_LOCK(state);
assert(Task_Check(state, task));
assert(task != &state->asyncio_tasks.first);
if (task->next != NULL) {
if (task->task_node.next != NULL) {
// already registered
assert(task->task_node.prev != NULL);
goto exit;
}
assert(task->prev == NULL);
assert(state->asyncio_tasks.head != NULL);
task->next = state->asyncio_tasks.head;
task->prev = state->asyncio_tasks.head->prev;
state->asyncio_tasks.head->prev->next = task;
state->asyncio_tasks.head->prev = task;
llist_insert_tail(&state->asyncio_tasks_head, &task->task_node);
exit:
ASYNCIO_STATE_UNLOCK(state);
}
@ -1924,18 +1906,12 @@ unregister_task(asyncio_state *state, TaskObj *task)
{
ASYNCIO_STATE_LOCK(state);
assert(Task_Check(state, task));
assert(task != &state->asyncio_tasks.first);
if (task->next == NULL) {
if (task->task_node.next == NULL) {
// not registered
assert(task->prev == NULL);
assert(state->asyncio_tasks.head != task);
assert(task->task_node.prev == NULL);
goto exit;
}
task->next->prev = task->prev;
task->prev->next = task->next;
task->next = NULL;
task->prev = NULL;
assert(state->asyncio_tasks.head != task);
llist_remove(&task->task_node);
exit:
ASYNCIO_STATE_UNLOCK(state);
}
@ -3625,20 +3601,18 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
Py_DECREF(eager_iter);
int err = 0;
ASYNCIO_STATE_LOCK(state);
TaskObj *first = &state->asyncio_tasks.first;
TaskObj *head = state->asyncio_tasks.head->next;
Py_INCREF(head);
while (head != first)
{
if (add_one_task(state, tasks, (PyObject *)head, loop) < 0) {
struct llist_node *node;
llist_for_each_safe(node, &state->asyncio_tasks_head) {
TaskObj *task = llist_data(node, TaskObj, task_node);
Py_INCREF(task);
if (add_one_task(state, tasks, (PyObject *)task, loop) < 0) {
Py_DECREF(task);
Py_DECREF(tasks);
Py_DECREF(loop);
Py_DECREF(head);
err = 1;
break;
}
Py_INCREF(head->next);
Py_SETREF(head, head->next);
Py_DECREF(task);
}
ASYNCIO_STATE_UNLOCK(state);
if (err) {
@ -3847,11 +3821,7 @@ module_exec(PyObject *mod)
{
asyncio_state *state = get_asyncio_state(mod);
Py_SET_TYPE(&state->asyncio_tasks.first, state->TaskType);
_Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.first);
state->asyncio_tasks.head = &state->asyncio_tasks.first;
state->asyncio_tasks.head->next = &state->asyncio_tasks.first;
state->asyncio_tasks.head->prev = &state->asyncio_tasks.first;
llist_init(&state->asyncio_tasks_head);
#define CREATE_TYPE(m, tp, spec, base) \
do { \