MagicStack / MagicStack/uvloop

Application hanging after KeyboardInterrupt

Offen
#335 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Vorherrschende Sprache
Cython
Sterne
11.9k
Forks
616
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

  • 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 <module>
    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 <pid>

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

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Führen Sie zunächst issue.py mit uvloop 0.14.0, Python 3.7.4 und aktiviertem PYTHONASYNCIODEBUG aus und vergleichen Sie anschließend dessen Verhalten bei Ctrl+C mit vanilla asyncio. Untersuchen Sie die im Snippet gezeigte Ausnahmebehandlung der Event-Schleife und den Pfad zum Herunterfahren nach einem Signal. Als erledigt gilt die Aufgabe, wenn KeyboardInterrupt eine ordnungsgemäße Bereinigung auslöst, ohne die Warnung zu einer nicht erwarteten Coroutine oder einen hängenden Prozess.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
backend
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
38/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.