Open
Description
Bug report
Bug description:
Blocking function running in thread executor will produce deadlock on shutdown after Ctrl+C pressed.
Here is minimal reproducing example:
import asyncio
import time
def blocking():
print("sleep start")
time.sleep(1000)
print("sleep end")
async def main():
await asyncio.to_thread(blocking)
asyncio.run(main())
Steps to reproduce:
- Run script
- Try press Ctrl+C when blocking func sleeps | Nothing produced
- If press Ctrl+C again | Will print KeyboardInterrupt exception but script will still hang
- If press Ctrl+C again | Will print one more KeyboardInterrupt and will shutdown.
(Tested on 3.9<=python<=3.12 on Ubuntu 22.04 all have same behaviour)
Output:
$ python3.11 test.py
sleep start
^C^CTraceback (most recent call last):
File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/ilya/test.py", line 13, in main
await asyncio.to_thread(blocking)
File "/usr/lib/python3.11/asyncio/threads.py", line 25, in to_thread
return await loop.run_in_executor(None, func_call)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/runners.py", line 123, in run
raise KeyboardInterrupt()
KeyboardInterrupt
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/ilya/test.py", line 15, in <module>
asyncio.run(main())
File "/usr/lib/python3.11/asyncio/runners.py", line 189, in run
with Runner(debug=debug) as runner:
File "/usr/lib/python3.11/asyncio/runners.py", line 63, in __exit__
self.close()
File "/usr/lib/python3.11/asyncio/runners.py", line 73, in close
loop.run_until_complete(loop.shutdown_default_executor())
File "/usr/lib/python3.11/asyncio/base_events.py", line 640, in run_until_complete
self.run_forever()
File "/usr/lib/python3.11/asyncio/base_events.py", line 607, in run_forever
self._run_once()
File "/usr/lib/python3.11/asyncio/base_events.py", line 1884, in _run_once
event_list = self._selector.select(timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/selectors.py", line 468, in select
fd_event_list = self._selector.poll(timeout, max_ev)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt
^CException ignored in: <module 'threading' from '/usr/lib/python3.11/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.11/threading.py", line 1560, in _shutdown
atexit_call()
File "/usr/lib/python3.11/concurrent/futures/thread.py", line 31, in _python_exit
t.join()
File "/usr/lib/python3.11/threading.py", line 1119, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.11/threading.py", line 1139, in _wait_for_tstate_lock
if lock.acquire(block, timeout):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt:
Additional:
Found a blog post https://www.roguelynn.com/words/asyncio-sync-and-threaded/ with looks like similar behavior back in 2019 where someone is trying to workaround this.
CPython versions tested on:
3.9, 3.10, 3.11, 3.12
Operating systems tested on:
Linux
Linked PRs
Metadata
Metadata
Assignees
Projects
Status
Todo