Skip to content

asyncio.all_tasks() omits eager tasks when called from another thread (FT-build) #152020

Description

@deadlovelll

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

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions