Skip to content

Commit

Permalink
On exit, cancel the main task first (#3805)
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 authored and asvetlov committed Oct 16, 2020
1 parent 7931b62 commit c2fb2b9
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 @@ -257,6 +257,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 = loop.create_task(_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 c2fb2b9

Please sign in to comment.