-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Closed as not planned
Labels
Description
Bug report
Bug description:
When using asyncio to launch subprocesses, and cancelling them before they are done, asyncio.run
sometimes hangs forever.
I can reproduce this fairly reliably on both macOS and Linux, though you may need to run this script a few times to hit the hang, so run it in a loop (eg. for i in $(seq 1 10); do python repro.py; done
):
import asyncio
import logging
from subprocess import PIPE
async def run_sleep():
proc = await asyncio.create_subprocess_exec(
"sleep",
"0.002",
stdout=PIPE,
)
await proc.communicate()
async def run_loop():
print("-- run_loop")
try:
async with asyncio.timeout(1):
pending = set[asyncio.Task]()
while True:
while len(pending) < 20:
pending.add(asyncio.create_task(run_sleep()))
_, pending = await asyncio.wait(
pending, return_when=asyncio.FIRST_COMPLETED
)
except asyncio.TimeoutError:
pass
finally:
print(f"-- exiting run_loop")
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
asyncio.run(run_loop())
When you hit this issue, you will see the program hangs forever instead of exiting. If I send SIGTERM
, I can see a traceback like this, showing it's stuck waiting for coroutines to finish cancelling:
Traceback (most recent call last):
File "/Users/obeattie/Desktop/repro.py", line 34, in <module>
asyncio.run(run_loop())
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 193, in run
with Runner(debug=debug, loop_factory=loop_factory) as runner:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 62, in __exit__
self.close()
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 70, in close
_cancel_all_tasks(loop)
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 205, in _cancel_all_tasks
loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True))
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 674, in run_until_complete
self.run_forever()
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 641, in run_forever
self._run_once()
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 1948, in _run_once
event_list = self._selector.select(timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/selectors.py", line 566, in select
kev_list = self._selector.control(None, max_ev, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task cancelling name='Task-4049' coro=<run_sleep() running at /Users/obeattie/Desktop/repro.py:7> wait_for=<Future pending cb=[Task.task_wakeup()]> cb=[gather.<locals>._done_callback() at /opt/homebrew/Cellar/python@3.12/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/tasks.py:767]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task cancelling name='Task-4050' coro=<run_sleep() running at /Users/obeattie/Desktop/repro.py:7> wait_for=<Future pending cb=[Task.task_wakeup()]> cb=[gather.<locals>._done_callback() at /opt/homebrew/Cellar/python@3.12/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/tasks.py:767]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task cancelling name='Task-4048' coro=<run_sleep() running at /Users/obeattie/Desktop/repro.py:7> wait_for=<Future pending cb=[Task.task_wakeup()]> cb=[gather.<locals>._done_callback() at /opt/homebrew/Cellar/python@3.12/3.12.7_1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/tasks.py:767]>
CPython versions tested on:
3.12
Operating systems tested on:
Linux, macOS
Metadata
Metadata
Assignees
Labels
Projects
Status
Done