https://github.com/python/cpython/commit/c9f75ebd907b92f4f56c180dd3b5ccf436d3d712
commit: c9f75ebd907b92f4f56c180dd3b5ccf436d3d712
branch: 3.14
author: Timofei <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-06-24T13:23:57Z
summary:

[3.14] gh-152020: Fix asyncio.all_tasks() loosing eager tasks on FT-build 
(GH-152022) (#152078)

[3.14] gh-152020: Fix `asyncio.all_tasks()` loosing eager tasks on FT-build 
(#152022)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-06-23-19-50-22.gh-issue-152020.DTKXjR.rst
M Lib/test/test_asyncio/test_free_threading.py
M Modules/_asynciomodule.c

diff --git a/Lib/test/test_asyncio/test_free_threading.py 
b/Lib/test/test_asyncio/test_free_threading.py
index d874ed00bd7e7ad..0e149dadd7f1219 100644
--- a/Lib/test/test_asyncio/test_free_threading.py
+++ b/Lib/test/test_asyncio/test_free_threading.py
@@ -165,6 +165,45 @@ async def main():
             loop.set_task_factory(self.factory)
             r.run(main())
 
+    def test_all_tasks_from_other_thread_includes_eager_tasks(self):
+        # gh-152020: all_tasks() called from another thread used to drop
+        # eager-started tasks on free-threaded builds.
+        loop = asyncio.new_event_loop()
+
+        async def wait_forever():
+            await asyncio.Event().wait()
+
+        def eager_factory(loop, coro, **kwargs):
+            return self.factory(loop, coro, eager_start=True, **kwargs)
+
+        async def setup():
+            loop.set_task_factory(eager_factory)
+            eager = loop.create_task(wait_forever(), name="EAGER")
+            loop.set_task_factory(None)
+            normal = loop.create_task(wait_forever(), name="NORMAL")
+            return eager, normal
+
+        async def teardown():
+            tasks = [t for t in asyncio.all_tasks()
+                     if t is not asyncio.current_task()]
+            for t in tasks:
+                t.cancel()
+            await asyncio.gather(*tasks, return_exceptions=True)
+
+        thread = threading.Thread(target=loop.run_forever)
+        thread.start()
+        try:
+            held = asyncio.run_coroutine_threadsafe(setup(), loop).result()
+            names = {t.get_name() for t in asyncio.all_tasks(loop)}
+            self.assertIn("NORMAL", names)
+            self.assertIn("EAGER", names)
+            del held
+        finally:
+            asyncio.run_coroutine_threadsafe(teardown(), loop).result()
+            loop.call_soon_threadsafe(loop.stop)
+            thread.join()
+            loop.close()
+
 
 class TestPyFreeThreading(TestFreeThreading, TestCase):
 
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-23-19-50-22.gh-issue-152020.DTKXjR.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-23-19-50-22.gh-issue-152020.DTKXjR.rst
new file mode 100644
index 000000000000000..93c716f7a6a1c86
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-23-19-50-22.gh-issue-152020.DTKXjR.rst
@@ -0,0 +1,3 @@
+On the free-threaded build, :func:`asyncio.all_tasks` no longer loses
+eager-started tasks when called from a thread other than the one running the
+event loop.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 1a4a41ef6fc9d5d..46a55f8b91d387e 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2385,7 +2385,11 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject 
*coro, PyObject *loop,
     if (self->task_name == NULL) {
         return -1;
     }
-
+#ifdef Py_GIL_DISABLED
+    // This is required so that _Py_TryIncref(self)
+    // works correctly in non-owning threads.
+    _PyObject_SetMaybeWeakref((PyObject *)self);
+#endif
     if (eager_start) {
         PyObject *res = PyObject_CallMethodNoArgs(loop, &_Py_ID(is_running));
         if (res == NULL) {
@@ -2404,11 +2408,6 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject 
*coro, PyObject *loop,
     if (task_call_step_soon(state, self, NULL)) {
         return -1;
     }
-#ifdef Py_GIL_DISABLED
-    // This is required so that _Py_TryIncref(self)
-    // works correctly in non-owning threads.
-    _PyObject_SetMaybeWeakref((PyObject *)self);
-#endif
     register_task(self);
     return 0;
 }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to