Skip to content

Commit 9eaa951

Browse files
kumaraditya303picnixz
authored andcommitted
pythonGH-107803: use circular double linked list for tasks in _asyncio (python#126577)
1 parent 8e58fd4 commit 9eaa951

File tree

1 file changed

+21
-52
lines changed

1 file changed

+21
-52
lines changed

Modules/_asynciomodule.c

+21-52
Original file line numberDiff line numberDiff line change
@@ -138,47 +138,18 @@ typedef struct {
138138
/* Counter for autogenerated Task names */
139139
uint64_t task_name_counter;
140140

141-
/* Linked-list of all tasks which are instances of asyncio.Task or subclasses
141+
/* Circular linked-list of all tasks which are instances of asyncio.Task or subclasses
142142
of it. Third party tasks implementations which don't inherit from
143143
asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet.
144-
`tail` is used as a sentinel to mark the end of the linked-list. It avoids one
144+
`first` is used as a sentinel to mark the end of the linked-list. It avoids one
145145
branch in checking for empty list when adding a new task, the list is
146-
initialized with `head` pointing to `tail` to mark an empty list.
147-
148-
Invariants:
149-
* When the list is empty:
150-
- asyncio_tasks.head == &asyncio_tasks.tail
151-
- asyncio_tasks.head->prev == NULL
152-
- asyncio_tasks.head->next == NULL
153-
154-
* After adding the first task 'task1':
155-
- asyncio_tasks.head == task1
156-
- task1->next == &asyncio_tasks.tail
157-
- task1->prev == NULL
158-
- asyncio_tasks.tail.prev == task1
159-
160-
* After adding a second task 'task2':
161-
- asyncio_tasks.head == task2
162-
- task2->next == task1
163-
- task2->prev == NULL
164-
- task1->prev == task2
165-
- asyncio_tasks.tail.prev == task1
166-
167-
* After removing task 'task1':
168-
- asyncio_tasks.head == task2
169-
- task2->next == &asyncio_tasks.tail
170-
- task2->prev == NULL
171-
- asyncio_tasks.tail.prev == task2
172-
173-
* After removing task 'task2', the list is empty:
174-
- asyncio_tasks.head == &asyncio_tasks.tail
175-
- asyncio_tasks.head->prev == NULL
176-
- asyncio_tasks.tail.prev == NULL
177-
- asyncio_tasks.tail.next == NULL
146+
initialized with `head`, `head->next` and `head->prev` pointing to `first`
147+
to mark an empty list.
148+
178149
*/
179150

180151
struct {
181-
TaskObj tail;
152+
TaskObj first;
182153
TaskObj *head;
183154
} asyncio_tasks;
184155

@@ -1925,7 +1896,7 @@ register_task(asyncio_state *state, TaskObj *task)
19251896
{
19261897
ASYNCIO_STATE_LOCK(state);
19271898
assert(Task_Check(state, task));
1928-
assert(task != &state->asyncio_tasks.tail);
1899+
assert(task != &state->asyncio_tasks.first);
19291900
if (task->next != NULL) {
19301901
// already registered
19311902
goto exit;
@@ -1934,8 +1905,10 @@ register_task(asyncio_state *state, TaskObj *task)
19341905
assert(state->asyncio_tasks.head != NULL);
19351906

19361907
task->next = state->asyncio_tasks.head;
1908+
task->prev = state->asyncio_tasks.head->prev;
1909+
state->asyncio_tasks.head->prev->next = task;
19371910
state->asyncio_tasks.head->prev = task;
1938-
state->asyncio_tasks.head = task;
1911+
19391912
exit:
19401913
ASYNCIO_STATE_UNLOCK(state);
19411914
}
@@ -1951,20 +1924,15 @@ unregister_task(asyncio_state *state, TaskObj *task)
19511924
{
19521925
ASYNCIO_STATE_LOCK(state);
19531926
assert(Task_Check(state, task));
1954-
assert(task != &state->asyncio_tasks.tail);
1927+
assert(task != &state->asyncio_tasks.first);
19551928
if (task->next == NULL) {
19561929
// not registered
19571930
assert(task->prev == NULL);
19581931
assert(state->asyncio_tasks.head != task);
19591932
goto exit;
19601933
}
19611934
task->next->prev = task->prev;
1962-
if (task->prev == NULL) {
1963-
assert(state->asyncio_tasks.head == task);
1964-
state->asyncio_tasks.head = task->next;
1965-
} else {
1966-
task->prev->next = task->next;
1967-
}
1935+
task->prev->next = task->next;
19681936
task->next = NULL;
19691937
task->prev = NULL;
19701938
assert(state->asyncio_tasks.head != task);
@@ -3657,12 +3625,10 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
36573625
Py_DECREF(eager_iter);
36583626
int err = 0;
36593627
ASYNCIO_STATE_LOCK(state);
3660-
TaskObj *head = state->asyncio_tasks.head;
3628+
TaskObj *first = &state->asyncio_tasks.first;
3629+
TaskObj *head = state->asyncio_tasks.head->next;
36613630
Py_INCREF(head);
3662-
assert(head != NULL);
3663-
assert(head->prev == NULL);
3664-
TaskObj *tail = &state->asyncio_tasks.tail;
3665-
while (head != tail)
3631+
while (head != first)
36663632
{
36673633
if (add_one_task(state, tasks, (PyObject *)head, loop) < 0) {
36683634
Py_DECREF(tasks);
@@ -3875,9 +3841,12 @@ static int
38753841
module_exec(PyObject *mod)
38763842
{
38773843
asyncio_state *state = get_asyncio_state(mod);
3878-
Py_SET_TYPE(&state->asyncio_tasks.tail, state->TaskType);
3879-
_Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.tail);
3880-
state->asyncio_tasks.head = &state->asyncio_tasks.tail;
3844+
3845+
Py_SET_TYPE(&state->asyncio_tasks.first, state->TaskType);
3846+
_Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.first);
3847+
state->asyncio_tasks.head = &state->asyncio_tasks.first;
3848+
state->asyncio_tasks.head->next = &state->asyncio_tasks.first;
3849+
state->asyncio_tasks.head->prev = &state->asyncio_tasks.first;
38813850

38823851
#define CREATE_TYPE(m, tp, spec, base) \
38833852
do { \

0 commit comments

Comments
 (0)