]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-105836: Fix `asyncio.run_coroutine_threadsafe` leaving underlying cancelled asynci...
authorKaisheng Xu <iaalmsimon@gmail.com>
Sat, 6 Dec 2025 19:33:25 +0000 (03:33 +0800)
committerGitHub <noreply@github.com>
Sat, 6 Dec 2025 19:33:25 +0000 (19:33 +0000)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Lib/asyncio/futures.py
Lib/test/test_asyncio/test_tasks.py
Misc/ACKS
Misc/NEWS.d/next/Library/2025-11-18-15-48-13.gh-issue-105836.sbUw24.rst [new file with mode: 0644]

index 6bd00a644789f1037a21beeca6ba0ddb895b235c..29652295218a2251ddc494e753fa5a7de9b35668 100644 (file)
@@ -389,7 +389,7 @@ def _chain_future(source, destination):
 
     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)
@@ -398,7 +398,7 @@ def _chain_future(source, destination):
         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():
index 931a43816a257a6538ef11bce8382b11c2222028..9809621a3244509d5b532fce64e5819434c3d28e 100644 (file)
@@ -3680,6 +3680,30 @@ class RunCoroutineThreadsafeTests(test_utils.TestCase):
         (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):
index ab6b8662d8fc81d25a90a7fef0bd2f5fc1019a7d..e3927ff0b3364e05458391e267e3d7bcd61a7f69 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -2119,6 +2119,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 (file)
index 0000000..d2edc5b
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :meth:`asyncio.run_coroutine_threadsafe` leaving underlying cancelled
+asyncio task running.