Skip to content

Commit

Permalink
On exit, cancel the main task first
Browse files Browse the repository at this point in the history
Otherwise, some tasks might be cancelled before cleanup hooks run. Fixes #3593
  • Loading branch information
multun committed Sep 6, 2019
1 parent 21b0621 commit e9fde6e
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGES/3805.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix tasks cancellation order on exit. The run_app task needs to be cancelled first for cleanup hooks to run with all tasks intact.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ Vaibhav Sagar
Vamsi Krishna Avula
Vasiliy Faronov
Vasyl Baran
Victor Collod
Victor Kovtun
Vikas Kawadia
Viktor Danyliuk
Expand Down
51 changes: 32 additions & 19 deletions aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@
from argparse import ArgumentParser
from collections.abc import Iterable
from importlib import import_module
from typing import Any, Awaitable, Callable, List, Optional, Type, Union, cast
from typing import (
Any,
Awaitable,
Callable,
List,
Optional,
Set,
Type,
Union,
cast,
)

from .abc import AbstractAccessLogger
from .helpers import all_tasks
Expand Down Expand Up @@ -368,8 +378,8 @@ async def _run_app(app: Union[Application, Awaitable[Application]], *,
await runner.cleanup()


def _cancel_all_tasks(loop: asyncio.AbstractEventLoop) -> None:
to_cancel = all_tasks(loop)
def _cancel_tasks(to_cancel: Set['asyncio.Task[Any]'],
loop: asyncio.AbstractEventLoop) -> None:
if not to_cancel:
return

Expand Down Expand Up @@ -416,25 +426,28 @@ def run_app(app: Union[Application, Awaitable[Application]], *,
access_log.addHandler(logging.StreamHandler())

try:
loop.run_until_complete(_run_app(app,
host=host,
port=port,
path=path,
sock=sock,
shutdown_timeout=shutdown_timeout,
ssl_context=ssl_context,
print=print,
backlog=backlog,
access_log_class=access_log_class,
access_log_format=access_log_format,
access_log=access_log,
handle_signals=handle_signals,
reuse_address=reuse_address,
reuse_port=reuse_port))
main_task = asyncio.ensure_future(_run_app(
app,
host=host,
port=port,
path=path,
sock=sock,
shutdown_timeout=shutdown_timeout,
ssl_context=ssl_context,
print=print,
backlog=backlog,
access_log_class=access_log_class,
access_log_format=access_log_format,
access_log=access_log,
handle_signals=handle_signals,
reuse_address=reuse_address,
reuse_port=reuse_port))
loop.run_until_complete(main_task)
except (GracefulExit, KeyboardInterrupt): # pragma: no cover
pass
finally:
_cancel_all_tasks(loop)
_cancel_tasks({main_task}, loop)
_cancel_tasks(all_tasks(loop), loop)
if sys.version_info >= (3, 6): # don't use PY_36 to pass mypy
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
Expand Down

0 comments on commit e9fde6e

Please sign in to comment.