Fix shutdown being delayed for cancelling tasks (#93287)

This commit is contained in:
J. Nick Koston 2023-05-21 07:17:34 -05:00 committed by GitHub
parent 72c76d35cc
commit 780f9bcdc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 4 deletions

View File

@ -82,7 +82,11 @@ from .exceptions import (
from .helpers.aiohttp_compat import restore_original_aiohttp_cancel_behavior
from .helpers.json import json_dumps
from .util import dt as dt_util, location, ulid as ulid_util
from .util.async_ import run_callback_threadsafe, shutdown_run_callback_threadsafe
from .util.async_ import (
cancelling,
run_callback_threadsafe,
shutdown_run_callback_threadsafe,
)
from .util.read_only_dict import ReadOnlyDict
from .util.timeout import TimeoutManager
from .util.unit_system import (
@ -678,7 +682,11 @@ class HomeAssistant:
start_time: float | None = None
current_task = asyncio.current_task()
while tasks := [task for task in self._tasks if task is not current_task]:
while tasks := [
task
for task in self._tasks
if task is not current_task and not cancelling(task)
]:
await self._await_and_log_pending(tasks)
if start_time is None:
@ -791,7 +799,7 @@ class HomeAssistant:
# while we are awaiting canceled tasks to get their result
# which will result in the set size changing during iteration
for task in list(running_tasks):
if task.done():
if task.done() or cancelling(task):
# Since we made a copy we need to check
# to see if the task finished while we
# were awaiting another task

View File

@ -1,7 +1,7 @@
"""Asyncio utilities."""
from __future__ import annotations
from asyncio import Semaphore, gather, get_running_loop
from asyncio import Future, Semaphore, gather, get_running_loop
from asyncio.events import AbstractEventLoop
from collections.abc import Awaitable, Callable
import concurrent.futures
@ -20,6 +20,13 @@ _R = TypeVar("_R")
_P = ParamSpec("_P")
def cancelling(task: Future[Any]) -> bool:
"""Return True if task is done or cancelling."""
# https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancelling
# is new in Python 3.11
return bool((cancelling_ := getattr(task, "cancelling", None)) and cancelling_())
def run_callback_threadsafe(
loop: AbstractEventLoop, callback: Callable[..., _T], *args: Any
) -> concurrent.futures.Future[_T]: