Skip to content

shutdown_default_executor / wait_for_tstate_lock deadlock (?) after Ctrl+C #111358

Open
@xbeastx

Description

@xbeastx

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:

  1. Run script
  2. Try press Ctrl+C when blocking func sleeps | Nothing produced
  3. If press Ctrl+C again | Will print KeyboardInterrupt exception but script will still hang
  4. 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

No one assigned

    Labels

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions