Skip to content

Commit

Permalink
pythongh-128002: use internal llist implementation for asyncio tasks (p…
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaraditya303 authored and srinivasreddy committed Jan 8, 2025
1 parent b3b2c5b commit f956778
Showing 1 changed file with 20 additions and 50 deletions.
70 changes: 20 additions & 50 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 *
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 { \
Expand Down

0 comments on commit f956778

Please sign in to comment.