MagicStack / MagicStack/uvloop

Application hanging after KeyboardInterrupt

未关闭
#335 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

主要语言
Cython
星标
11.9k
派生
616
PR 合并指标
30 天内没有已合并 PR

描述

  • 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

贡献指南

这个仓库没有索引到贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先,在启用 PYTHONASYNCIODEBUG 的情况下,使用 uvloop 0.14.0 和 Python 3.7.4 运行 issue.py,然后将其对 Ctrl+C 的行为与 vanilla asyncio 进行比较。调查片段中所示的事件循环异常处理和信号关闭路径。完成的标准是:KeyboardInterrupt 能触发正常清理,且不会出现未等待 coroutine 的警告或进程挂起。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
backend
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
38/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。