MagicStack / MagicStack/uvloop

Application hanging after KeyboardInterrupt

Open
#335 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Cython
Stars
11.9k
Forks
616
PR merge metrics
No merged PRs in 30d

Description

* **uvloop version**: 0.14.0
* **Python version**: 3.7.4
* **Platform**: Manjaro
* **Can you reproduce the bug with `PYTHONASYNCIODEBUG` in env?**: Yes
* **Does uvloop behave differently from vanilla asyncio? How?**: With vanilla, the application terminates gracefully upon reception of a keyboard interrupt. If I use `uvloop`, I get a runtime warning and the application hangs until I run a `kill -9` on the process id.

Snippet: `issue.py`
```
import asyncio
from asyncio import AbstractEventLoop
import os
from typing import Union, Type

import uvloop # type: ignore
from aiohttp import web
import signal

import aiologger

logger = aiologger.Logger.with_default_handlers()

async def handle_exception(loop: AbstractEventLoop, context):
# context["message"] will always be there; but context["exception"] may not
msg = context.get("exception", context["message"])
await logger.error(f"Caught exception: {msg}")
await logger.info("Shutting down...")
asyncio.create_task(shutdown(loop))

async def shutdown(loop: AbstractEventLoop, signal=None):
"""Cleanup tasks tied to the service's shutdown."""
if signal:
await logger.info(f"Received exit signal {signal.name}...")

tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]

[task.cancel() for task in tasks]

await logger.info(f"Cancelling {len(tasks)} outstanding tasks")
await asyncio.gather(*tasks, return_exceptions=True)
loop.stop()

async def start(
app: web.Application, host: str, port: Union[str, int]
) -> web.AppRunner:
"""Start the server"""
runner = web.AppRunner(app)
await runner.setup()
server = web.TCPSite(runner, host, int(port))
await server.start()
return runner

def main() -> None:
"""Entrypoint"""
host = os.environ.get("HOST", "localhost")
port = os.environ.get("PORT", 8000)
app = web.Application()
loop = asyncio.get_event_loop()
signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
for s in signals:
loop.add_signal_handler(
s, lambda s=s: asyncio.create_task(shutdown(loop, signal=s))
)
loop.set_exception_handler(handle_exception)
print(
f"======== Running on http://{host}:{port} ========\n" "(Press CTRL+C to quit)"
)
try:
runner = loop.run_until_complete(start(app, host, port))
loop.run_forever()
finally:
loop.run_until_complete(runner.cleanup())
loop.close()

if __name__ == "__main__":
uvloop.install()
main()

```

Run the file:
`python issue.py`

Then hit Ctrl+c on keyboard, output:
```
======== Running on http://0.0.0.0:8000 ========
(Press CTRL+C to quit)
^Cissue.py:70: RuntimeWarning: coroutine 'handle_exception' was never awaited
Coroutine created at (most recent call last)
File "issue.py", line 78, in
main()
File "issue.py", line 70, in main
loop.run_forever()
loop.run_forever()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
```
Which hangs until running `kill -9 `

If you comment out `uvloop.install()` and follow the same steps, the program terminates as expected. Output:
```
======== Running on http://0.0.0.0:8000 ========
(Press CTRL+C to quit)
^CReceived exit signal SIGINT...
Cancelling 0 outstanding tasks

```

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reported issue.py with uvloop 0.14.0, Python 3.7.4, and PYTHONASYNCIODEBUG enabled, then compare its Ctrl+C behavior with vanilla asyncio. Investigate the event-loop exception handling and signal shutdown path shown in the snippet. Done means KeyboardInterrupt triggers graceful cleanup without the unawaited-coroutine warning or a hanging process.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.