From a794c09a0f44dbe375c544b0bf14d85557463ff4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 7 Jul 2021 02:23:24 -0500 Subject: [PATCH] Fix deadlock at shutdown with python 3.9 (#52613) --- homeassistant/runner.py | 10 ---------- homeassistant/util/executor.py | 2 +- tests/util/test_executor.py | 12 ++++++------ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/homeassistant/runner.py b/homeassistant/runner.py index 86bebecb7b1..5eae0b1b2da 100644 --- a/homeassistant/runner.py +++ b/homeassistant/runner.py @@ -73,16 +73,6 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[valid loop.set_default_executor = warn_use( # type: ignore loop.set_default_executor, "sets default executor on the event loop" ) - - # Shut down executor when we shut down loop - orig_close = loop.close - - def close() -> None: - executor.logged_shutdown() - orig_close() - - loop.close = close # type: ignore - return loop diff --git a/homeassistant/util/executor.py b/homeassistant/util/executor.py index c25c6b9c13f..9277e396bc4 100644 --- a/homeassistant/util/executor.py +++ b/homeassistant/util/executor.py @@ -62,7 +62,7 @@ def join_or_interrupt_threads( class InterruptibleThreadPoolExecutor(ThreadPoolExecutor): """A ThreadPoolExecutor instance that will not deadlock on shutdown.""" - def logged_shutdown(self) -> None: + def shutdown(self, *args, **kwargs) -> None: # type: ignore """Shutdown backport from cpython 3.9 with interrupt support added.""" with self._shutdown_lock: # type: ignore[attr-defined] self._shutdown = True diff --git a/tests/util/test_executor.py b/tests/util/test_executor.py index 911145ecc4e..eaa48c75d1a 100644 --- a/tests/util/test_executor.py +++ b/tests/util/test_executor.py @@ -24,7 +24,7 @@ async def test_executor_shutdown_can_interrupt_threads(caplog): for _ in range(100): sleep_futures.append(iexecutor.submit(_loop_sleep_in_executor)) - iexecutor.logged_shutdown() + iexecutor.shutdown() for future in sleep_futures: with pytest.raises((concurrent.futures.CancelledError, SystemExit)): @@ -45,13 +45,13 @@ async def test_executor_shutdown_only_logs_max_attempts(caplog): iexecutor.submit(_loop_sleep_in_executor) with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.3): - iexecutor.logged_shutdown() + iexecutor.shutdown() assert "time.sleep(0.2)" in caplog.text assert ( caplog.text.count("is still running at shutdown") == executor.MAX_LOG_ATTEMPTS ) - iexecutor.logged_shutdown() + iexecutor.shutdown() async def test_executor_shutdown_does_not_log_shutdown_on_first_attempt(caplog): @@ -65,7 +65,7 @@ async def test_executor_shutdown_does_not_log_shutdown_on_first_attempt(caplog): for _ in range(5): iexecutor.submit(_do_nothing) - iexecutor.logged_shutdown() + iexecutor.shutdown() assert "is still running at shutdown" not in caplog.text @@ -83,9 +83,9 @@ async def test_overall_timeout_reached(caplog): start = time.monotonic() with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.5): - iexecutor.logged_shutdown() + iexecutor.shutdown() finish = time.monotonic() assert finish - start < 1 - iexecutor.logged_shutdown() + iexecutor.shutdown()