gh-105836: Fix asyncio.run_coroutine_threadsafe leaving underlying cancelled asyncio task running (#141696)

Co-authored-by: Kumar Aditya <kumaraditya@python.org>
This commit is contained in:
Kaisheng Xu 2025-12-07 03:33:25 +08:00 committed by GitHub
parent 56a442d0d8
commit 14715e3a64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 2 deletions

View file

@ -389,7 +389,7 @@ def _set_state(future, other):
def _call_check_cancel(destination): def _call_check_cancel(destination):
if destination.cancelled(): 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() source.cancel()
else: else:
source_loop.call_soon_threadsafe(source.cancel) source_loop.call_soon_threadsafe(source.cancel)
@ -398,7 +398,7 @@ def _call_set_state(source):
if (destination.cancelled() and if (destination.cancelled() and
dest_loop is not None and dest_loop.is_closed()): dest_loop is not None and dest_loop.is_closed()):
return 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) _set_state(destination, source)
else: else:
if dest_loop.is_closed(): if dest_loop.is_closed():

View file

@ -3680,6 +3680,30 @@ def task_factory(loop, coro):
(loop, context), kwargs = callback.call_args (loop, context), kwargs = callback.call_args
self.assertEqual(context['exception'], exc_context.exception) 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): class SleepTests(test_utils.TestCase):
def setUp(self): def setUp(self):

View file

@ -2119,6 +2119,7 @@ Xiang Zhang
Robert Xiao Robert Xiao
Florent Xicluna Florent Xicluna
Yanbo, Xie Yanbo, Xie
Kaisheng Xu
Xinhang Xu Xinhang Xu
Arnon Yaari Arnon Yaari
Alakshendra Yadav Alakshendra Yadav

View file

@ -0,0 +1,2 @@
Fix :meth:`asyncio.run_coroutine_threadsafe` leaving underlying cancelled
asyncio task running.