Fix deadlock at shutdown with python 3.9 (#52613)
parent
a7ee86730c
commit
a794c09a0f
|
@ -73,16 +73,6 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[valid
|
||||||
loop.set_default_executor = warn_use( # type: ignore
|
loop.set_default_executor = warn_use( # type: ignore
|
||||||
loop.set_default_executor, "sets default executor on the event loop"
|
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
|
return loop
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ def join_or_interrupt_threads(
|
||||||
class InterruptibleThreadPoolExecutor(ThreadPoolExecutor):
|
class InterruptibleThreadPoolExecutor(ThreadPoolExecutor):
|
||||||
"""A ThreadPoolExecutor instance that will not deadlock on shutdown."""
|
"""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."""
|
"""Shutdown backport from cpython 3.9 with interrupt support added."""
|
||||||
with self._shutdown_lock: # type: ignore[attr-defined]
|
with self._shutdown_lock: # type: ignore[attr-defined]
|
||||||
self._shutdown = True
|
self._shutdown = True
|
||||||
|
|
|
@ -24,7 +24,7 @@ async def test_executor_shutdown_can_interrupt_threads(caplog):
|
||||||
for _ in range(100):
|
for _ in range(100):
|
||||||
sleep_futures.append(iexecutor.submit(_loop_sleep_in_executor))
|
sleep_futures.append(iexecutor.submit(_loop_sleep_in_executor))
|
||||||
|
|
||||||
iexecutor.logged_shutdown()
|
iexecutor.shutdown()
|
||||||
|
|
||||||
for future in sleep_futures:
|
for future in sleep_futures:
|
||||||
with pytest.raises((concurrent.futures.CancelledError, SystemExit)):
|
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)
|
iexecutor.submit(_loop_sleep_in_executor)
|
||||||
|
|
||||||
with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.3):
|
with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.3):
|
||||||
iexecutor.logged_shutdown()
|
iexecutor.shutdown()
|
||||||
|
|
||||||
assert "time.sleep(0.2)" in caplog.text
|
assert "time.sleep(0.2)" in caplog.text
|
||||||
assert (
|
assert (
|
||||||
caplog.text.count("is still running at shutdown") == executor.MAX_LOG_ATTEMPTS
|
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):
|
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):
|
for _ in range(5):
|
||||||
iexecutor.submit(_do_nothing)
|
iexecutor.submit(_do_nothing)
|
||||||
|
|
||||||
iexecutor.logged_shutdown()
|
iexecutor.shutdown()
|
||||||
|
|
||||||
assert "is still running at shutdown" not in caplog.text
|
assert "is still running at shutdown" not in caplog.text
|
||||||
|
|
||||||
|
@ -83,9 +83,9 @@ async def test_overall_timeout_reached(caplog):
|
||||||
|
|
||||||
start = time.monotonic()
|
start = time.monotonic()
|
||||||
with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.5):
|
with patch.object(executor, "EXECUTOR_SHUTDOWN_TIMEOUT", 0.5):
|
||||||
iexecutor.logged_shutdown()
|
iexecutor.shutdown()
|
||||||
finish = time.monotonic()
|
finish = time.monotonic()
|
||||||
|
|
||||||
assert finish - start < 1
|
assert finish - start < 1
|
||||||
|
|
||||||
iexecutor.logged_shutdown()
|
iexecutor.shutdown()
|
||||||
|
|
Loading…
Reference in New Issue