]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128002: use internal llist implementation for asyncio tasks (#128256)
authorKumar Aditya <kumaraditya@python.org>
Wed, 25 Dec 2024 12:21:27 +0000 (17:51 +0530)
committerGitHub <noreply@github.com>
Wed, 25 Dec 2024 12:21:27 +0000 (17:51 +0530)
Modules/_asynciomodule.c

index 27c163644573365379927581a6b921131afaea49..603b77a70b15d4db64a78bd724b4cad2ae2fbecb 100644 (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 {                                                                \