MagicStack / MagicStack/uvloop

Application hanging after KeyboardInterrupt

Ouverte
#335 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Langage dominant
Cython
Étoiles
11.9k
Forks
616
Métriques de merge des PR
Aucune PR mergée en 30 j

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 <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

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par exécuter issue.py avec uvloop 0.14.0, Python 3.7.4 et PYTHONASYNCIODEBUG activé, puis comparez son comportement avec Ctrl+C à celui de vanilla asyncio. Examinez la gestion des exceptions de la boucle d’événements et le chemin d’arrêt sur signal présentés dans l’extrait. Le travail est terminé lorsque KeyboardInterrupt déclenche un nettoyage propre, sans l’avertissement concernant une coroutine non attendue ni un processus qui reste bloqué.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
backend
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
38/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.