1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00

Hold a lock to prevent concurrent setup of config entries (#116482)

This commit is contained in:
J. Nick Koston
2024-04-30 18:47:12 -05:00
committed by GitHub
parent 3c7cbf5794
commit 6cf1c5c1f2
7 changed files with 129 additions and 18 deletions

View File

@@ -295,7 +295,7 @@ class ConfigEntry(Generic[_DataT]):
update_listeners: list[UpdateListenerType]
_async_cancel_retry_setup: Callable[[], Any] | None
_on_unload: list[Callable[[], Coroutine[Any, Any, None] | None]] | None
reload_lock: asyncio.Lock
setup_lock: asyncio.Lock
_reauth_lock: asyncio.Lock
_reconfigure_lock: asyncio.Lock
_tasks: set[asyncio.Future[Any]]
@@ -403,7 +403,7 @@ class ConfigEntry(Generic[_DataT]):
_setter(self, "_on_unload", None)
# Reload lock to prevent conflicting reloads
_setter(self, "reload_lock", asyncio.Lock())
_setter(self, "setup_lock", asyncio.Lock())
# Reauth lock to prevent concurrent reauth flows
_setter(self, "_reauth_lock", asyncio.Lock())
# Reconfigure lock to prevent concurrent reconfigure flows
@@ -702,19 +702,17 @@ class ConfigEntry(Generic[_DataT]):
# has started so we do not block shutdown
if not hass.is_stopping:
hass.async_create_background_task(
self._async_setup_retry(hass),
self.async_setup_locked(hass),
f"config entry retry {self.domain} {self.title}",
eager_start=True,
)
async def _async_setup_retry(self, hass: HomeAssistant) -> None:
"""Retry setup.
We hold the reload lock during setup retry to ensure
that nothing can reload the entry while we are retrying.
"""
async with self.reload_lock:
await self.async_setup(hass)
async def async_setup_locked(
self, hass: HomeAssistant, integration: loader.Integration | None = None
) -> None:
"""Set up while holding the setup lock."""
async with self.setup_lock:
await self.async_setup(hass, integration=integration)
@callback
def async_shutdown(self) -> None:
@@ -1794,7 +1792,15 @@ class ConfigEntries:
# attempts.
entry.async_cancel_retry_setup()
async with entry.reload_lock:
if entry.domain not in self.hass.config.components:
# If the component is not loaded, just load it as
# the config entry will be loaded as well. We need
# to do this before holding the lock to avoid a
# deadlock.
await async_setup_component(self.hass, entry.domain, self._hass_config)
return entry.state is ConfigEntryState.LOADED
async with entry.setup_lock:
unload_result = await self.async_unload(entry_id)
if not unload_result or entry.disabled_by: