#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()
PyObject *task_coro;
PyObject *task_name;
PyObject *task_context;
- struct TaskObj *next;
- struct TaskObj *prev;
+ struct llist_node task_node;
} TaskObj;
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 *
{
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);
}
{
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);
}
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) {
{
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 { \