Skip to content

Commit 76f1785

Browse files
gh-128002: use internal llist implementation for asyncio tasks (#128256)
1 parent d9ed42b commit 76f1785

File tree

1 file changed

+20
-50
lines changed

1 file changed

+20
-50
lines changed

Modules/_asynciomodule.c

+20-50
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_MUT()
77
#include "pycore_dict.h" // _PyDict_GetItem_KnownHash()
88
#include "pycore_freelist.h" // _Py_FREELIST_POP()
9+
#include "pycore_llist.h" // struct llist_node
910
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
1011
#include "pycore_moduleobject.h" // _PyModule_GetState()
11-
#include "pycore_object.h" // _Py_SetImmortalUntracked()
1212
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
1313
#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing()
1414
#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -60,8 +60,7 @@ typedef struct TaskObj {
6060
PyObject *task_coro;
6161
PyObject *task_name;
6262
PyObject *task_context;
63-
struct TaskObj *next;
64-
struct TaskObj *prev;
63+
struct llist_node task_node;
6564
} TaskObj;
6665

6766
typedef struct {
@@ -136,21 +135,11 @@ typedef struct {
136135
/* Counter for autogenerated Task names */
137136
uint64_t task_name_counter;
138137

139-
/* Circular linked-list of all tasks which are instances of asyncio.Task or subclasses
140-
of it. Third party tasks implementations which don't inherit from
141-
asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet.
142-
`first` is used as a sentinel to mark the end of the linked-list. It avoids one
143-
branch in checking for empty list when adding a new task, the list is
144-
initialized with `head`, `head->next` and `head->prev` pointing to `first`
145-
to mark an empty list.
146-
138+
/* Head of circular linked-list of all tasks which are instances of `asyncio.Task`
139+
or subclasses of it. Third party tasks implementations which don't inherit from
140+
`asyncio.Task` are tracked separately using the `non_asyncio_tasks` WeakSet.
147141
*/
148-
149-
struct {
150-
TaskObj first;
151-
TaskObj *head;
152-
} asyncio_tasks;
153-
142+
struct llist_node asyncio_tasks_head;
154143
} asyncio_state;
155144

156145
static inline asyncio_state *
@@ -1896,19 +1885,12 @@ register_task(asyncio_state *state, TaskObj *task)
18961885
{
18971886
ASYNCIO_STATE_LOCK(state);
18981887
assert(Task_Check(state, task));
1899-
assert(task != &state->asyncio_tasks.first);
1900-
if (task->next != NULL) {
1888+
if (task->task_node.next != NULL) {
19011889
// already registered
1890+
assert(task->task_node.prev != NULL);
19021891
goto exit;
19031892
}
1904-
assert(task->prev == NULL);
1905-
assert(state->asyncio_tasks.head != NULL);
1906-
1907-
task->next = state->asyncio_tasks.head;
1908-
task->prev = state->asyncio_tasks.head->prev;
1909-
state->asyncio_tasks.head->prev->next = task;
1910-
state->asyncio_tasks.head->prev = task;
1911-
1893+
llist_insert_tail(&state->asyncio_tasks_head, &task->task_node);
19121894
exit:
19131895
ASYNCIO_STATE_UNLOCK(state);
19141896
}
@@ -1924,18 +1906,12 @@ unregister_task(asyncio_state *state, TaskObj *task)
19241906
{
19251907
ASYNCIO_STATE_LOCK(state);
19261908
assert(Task_Check(state, task));
1927-
assert(task != &state->asyncio_tasks.first);
1928-
if (task->next == NULL) {
1909+
if (task->task_node.next == NULL) {
19291910
// not registered
1930-
assert(task->prev == NULL);
1931-
assert(state->asyncio_tasks.head != task);
1911+
assert(task->task_node.prev == NULL);
19321912
goto exit;
19331913
}
1934-
task->next->prev = task->prev;
1935-
task->prev->next = task->next;
1936-
task->next = NULL;
1937-
task->prev = NULL;
1938-
assert(state->asyncio_tasks.head != task);
1914+
llist_remove(&task->task_node);
19391915
exit:
19401916
ASYNCIO_STATE_UNLOCK(state);
19411917
}
@@ -3625,20 +3601,18 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
36253601
Py_DECREF(eager_iter);
36263602
int err = 0;
36273603
ASYNCIO_STATE_LOCK(state);
3628-
TaskObj *first = &state->asyncio_tasks.first;
3629-
TaskObj *head = state->asyncio_tasks.head->next;
3630-
Py_INCREF(head);
3631-
while (head != first)
3632-
{
3633-
if (add_one_task(state, tasks, (PyObject *)head, loop) < 0) {
3604+
struct llist_node *node;
3605+
llist_for_each_safe(node, &state->asyncio_tasks_head) {
3606+
TaskObj *task = llist_data(node, TaskObj, task_node);
3607+
Py_INCREF(task);
3608+
if (add_one_task(state, tasks, (PyObject *)task, loop) < 0) {
3609+
Py_DECREF(task);
36343610
Py_DECREF(tasks);
36353611
Py_DECREF(loop);
3636-
Py_DECREF(head);
36373612
err = 1;
36383613
break;
36393614
}
3640-
Py_INCREF(head->next);
3641-
Py_SETREF(head, head->next);
3615+
Py_DECREF(task);
36423616
}
36433617
ASYNCIO_STATE_UNLOCK(state);
36443618
if (err) {
@@ -3847,11 +3821,7 @@ module_exec(PyObject *mod)
38473821
{
38483822
asyncio_state *state = get_asyncio_state(mod);
38493823

3850-
Py_SET_TYPE(&state->asyncio_tasks.first, state->TaskType);
3851-
_Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.first);
3852-
state->asyncio_tasks.head = &state->asyncio_tasks.first;
3853-
state->asyncio_tasks.head->next = &state->asyncio_tasks.first;
3854-
state->asyncio_tasks.head->prev = &state->asyncio_tasks.first;
3824+
llist_init(&state->asyncio_tasks_head);
38553825

38563826
#define CREATE_TYPE(m, tp, spec, base) \
38573827
do { \

0 commit comments

Comments
 (0)