Skip to content

Commit 82e7a74

Browse files
deadlovelllkumaraditya303
authored andcommitted
pythongh-152020: Fix asyncio.all_tasks() loosing eager tasks on FT-build (pythonGH-152022)
(cherry picked from commit ad2cabf) Co-authored-by: Timofei <128279579+deadlovelll@users.noreply.github.com> Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent e8b3439 commit 82e7a74

3 files changed

Lines changed: 47 additions & 5 deletions

File tree

Lib/test/test_asyncio/test_free_threading.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,45 @@ async def main():
165165
loop.set_task_factory(self.factory)
166166
r.run(main())
167167

168+
def test_all_tasks_from_other_thread_includes_eager_tasks(self):
169+
# gh-152020: all_tasks() called from another thread used to drop
170+
# eager-started tasks on free-threaded builds.
171+
loop = asyncio.new_event_loop()
172+
173+
async def wait_forever():
174+
await asyncio.Event().wait()
175+
176+
def eager_factory(loop, coro, **kwargs):
177+
return self.factory(loop, coro, eager_start=True, **kwargs)
178+
179+
async def setup():
180+
loop.set_task_factory(eager_factory)
181+
eager = loop.create_task(wait_forever(), name="EAGER")
182+
loop.set_task_factory(None)
183+
normal = loop.create_task(wait_forever(), name="NORMAL")
184+
return eager, normal
185+
186+
async def teardown():
187+
tasks = [t for t in asyncio.all_tasks()
188+
if t is not asyncio.current_task()]
189+
for t in tasks:
190+
t.cancel()
191+
await asyncio.gather(*tasks, return_exceptions=True)
192+
193+
thread = threading.Thread(target=loop.run_forever)
194+
thread.start()
195+
try:
196+
held = asyncio.run_coroutine_threadsafe(setup(), loop).result()
197+
names = {t.get_name() for t in asyncio.all_tasks(loop)}
198+
self.assertIn("NORMAL", names)
199+
self.assertIn("EAGER", names)
200+
del held
201+
finally:
202+
asyncio.run_coroutine_threadsafe(teardown(), loop).result()
203+
loop.call_soon_threadsafe(loop.stop)
204+
thread.join()
205+
loop.close()
206+
168207

169208
class TestPyFreeThreading(TestFreeThreading, TestCase):
170209

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
On the free-threaded build, :func:`asyncio.all_tasks` no longer loses
2+
eager-started tasks when called from a thread other than the one running the
3+
event loop.

Modules/_asynciomodule.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,6 +2366,11 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
23662366
return -1;
23672367
}
23682368
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET();
2369+
#ifdef Py_GIL_DISABLED
2370+
// This is required so that _Py_TryIncref(self)
2371+
// works correctly in non-owning threads.
2372+
_PyObject_SetMaybeWeakref((PyObject *)self);
2373+
#endif
23692374
if (eager_start) {
23702375
PyObject *res = PyObject_CallMethodNoArgs(loop, &_Py_ID(is_running));
23712376
if (res == NULL) {
@@ -2384,11 +2389,6 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
23842389
if (task_call_step_soon(state, self, NULL)) {
23852390
return -1;
23862391
}
2387-
#ifdef Py_GIL_DISABLED
2388-
// This is required so that _Py_TryIncref(self)
2389-
// works correctly in non-owning threads.
2390-
_PyObject_SetMaybeWeakref((PyObject *)self);
2391-
#endif
23922392
register_task(ts, self);
23932393
return 0;
23942394
}

0 commit comments

Comments
 (0)