Bug report
Bug description:
On a free-threading build asyncio.all_tasks() called from a thread other than the one running loop silently drops eager-started tasks
Reproducer:
import asyncio import sys import threading async def forever(): await asyncio.Event().wait() async def setup(): loop = asyncio.get_running_loop() loop.set_task_factory(asyncio.eager_task_factory) loop.create_task(forever(), name="EAGER") loop.set_task_factory(None) loop.create_task(forever(), name="NORMAL") loop = asyncio.new_event_loop() threading.Thread(target=loop.run_forever, daemon=True).start() asyncio.run_coroutine_threadsafe(setup(), loop).result() print("GIL enabled:", sys._is_gil_enabled()) print("all_tasks:", sorted(t.get_name() for t in asyncio.all_tasks(loop)))
Expected output is all_tasks: ['EAGER', 'NORMAL'] but actually we have all_tasks: ['NORMAL']
Proposed fix is to add _PyObject_SetMaybeWeakref in register_task
static void register_task(_PyThreadStateImpl *ts, TaskObj *task) { if (task->task_node.next != NULL) { // already registered assert(task->task_node.prev != NULL); return; } #ifdef Py_GIL_DISABLED _PyObject_SetMaybeWeakref((PyObject *)task); #endif struct llist_node *head = &ts->asyncio_tasks_head; llist_insert_tail(head, &task->task_node); }
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS