Skip to content
Merged
4 changes: 2 additions & 2 deletions Lib/asyncio/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,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)
Expand All @@ -398,7 +398,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():
Expand Down
19 changes: 19 additions & 0 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import random
import re
import sys
from threading import Event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move this import into the test function -- otherwise it could be confused with asyncio.Event.

import traceback
import types
import unittest
Expand Down Expand Up @@ -3680,6 +3681,24 @@ 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):
target_started = Event()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use import threading followed by target_started = threading.Event() to be super clear about what kind of event this is.

async def _target():
target_started.set()
await asyncio.sleep(0.1)
return 1

def _in_thread():
thread_future = asyncio.run_coroutine_threadsafe(_target(), self.loop)
# wait target started then cancel
target_started.wait()
thread_future.cancel()
_ = self.loop.run_in_executor(None, _in_thread)

# start main loop to do things
self.loop.run_until_complete(asyncio.sleep(0.05))
self.assertEqual(0, len(self.loop._ready))


class SleepTests(test_utils.TestCase):
def setUp(self):
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,7 @@ Xiang Zhang
Robert Xiao
Florent Xicluna
Yanbo, Xie
Kaisheng Xu
Xinhang Xu
Arnon Yaari
Alakshendra Yadav
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :meth:`asyncio.run_coroutine_threadsafe` leaving underlying cancelled
asyncio task running
Loading