https://github.com/python/cpython/commit/b2fff0ea858077a7bbd336e27ae3bd3d25163da2 commit: b2fff0ea858077a7bbd336e27ae3bd3d25163da2 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: kumaraditya303 <[email protected]> date: 2025-12-09T20:15:50+05:30 summary:
[3.13] gh-105836: Fix `asyncio.run_coroutine_threadsafe` leaving underlying cancelled asyncio task running (GH-141696) (#142359) gh-105836: Fix `asyncio.run_coroutine_threadsafe` leaving underlying cancelled asyncio task running (GH-141696) (cherry picked from commit 14715e3a64a674629c781d4a3dd11143ba010990) Co-authored-by: Kaisheng Xu <[email protected]> Co-authored-by: Kumar Aditya <[email protected]> files: A Misc/NEWS.d/next/Library/2025-11-18-15-48-13.gh-issue-105836.sbUw24.rst M Lib/asyncio/futures.py M Lib/test/test_asyncio/test_tasks.py M Misc/ACKS diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 51932639097bbd..e24e16de751b9f 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -383,7 +383,7 @@ def _set_state(future, other): def _call_check_cancel(destination): if destination.cancelled(): - if source_loop is None or source_loop is dest_loop: + if source_loop is None or source_loop is events._get_running_loop(): source.cancel() else: source_loop.call_soon_threadsafe(source.cancel) @@ -392,7 +392,7 @@ def _call_set_state(source): if (destination.cancelled() and dest_loop is not None and dest_loop.is_closed()): return - if dest_loop is None or dest_loop is source_loop: + if dest_loop is None or dest_loop is events._get_running_loop(): _set_state(destination, source) else: if dest_loop.is_closed(): diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 54692713011278..361ad8a7874077 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -3552,6 +3552,30 @@ def task_factory(loop, coro): (loop, context), kwargs = callback.call_args self.assertEqual(context['exception'], exc_context.exception) + def test_run_coroutine_threadsafe_and_cancel(self): + task = None + thread_future = None + # Use a custom task factory to capture the created Task + def task_factory(loop, coro): + nonlocal task + task = asyncio.Task(coro, loop=loop) + return task + + self.addCleanup(self.loop.set_task_factory, + self.loop.get_task_factory()) + + async def target(): + nonlocal thread_future + self.loop.set_task_factory(task_factory) + thread_future = asyncio.run_coroutine_threadsafe(asyncio.sleep(10), self.loop) + await asyncio.sleep(0) + + thread_future.cancel() + + self.loop.run_until_complete(target()) + self.assertTrue(task.cancelled()) + self.assertTrue(thread_future.cancelled()) + class SleepTests(test_utils.TestCase): def setUp(self): diff --git a/Misc/ACKS b/Misc/ACKS index 6cc30812cced11..55713d3b286835 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -2074,6 +2074,7 @@ Xiang Zhang Robert Xiao Florent Xicluna Yanbo, Xie +Kaisheng Xu Xinhang Xu Arnon Yaari Alakshendra Yadav diff --git a/Misc/NEWS.d/next/Library/2025-11-18-15-48-13.gh-issue-105836.sbUw24.rst b/Misc/NEWS.d/next/Library/2025-11-18-15-48-13.gh-issue-105836.sbUw24.rst new file mode 100644 index 00000000000000..d2edc5b2cb743d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-18-15-48-13.gh-issue-105836.sbUw24.rst @@ -0,0 +1,2 @@ +Fix :meth:`asyncio.run_coroutine_threadsafe` leaving underlying cancelled +asyncio task running. _______________________________________________ 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]
