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]