Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3daeda1
Avoid creating tasks to remove entities
bdraco Feb 19, 2024
4db6cb3
Merge branch 'dev' into entity_removal_never_normally_suspends
bdraco Feb 19, 2024
dee4383
reload in task
bdraco Feb 19, 2024
b96f118
Merge remote-tracking branch 'upstream/dev' into entity_removal_never…
bdraco Feb 19, 2024
b09979e
Merge remote-tracking branch 'upstream/entity_removal_never_normally_…
bdraco Feb 19, 2024
a833a7f
revert, change test
bdraco Feb 19, 2024
76c89ea
revert, change test
bdraco Feb 19, 2024
6f7fc2c
better fix
bdraco Feb 19, 2024
14c02cf
fix race
bdraco Feb 19, 2024
34191cf
collapse
bdraco Feb 19, 2024
8b7795d
comment
bdraco Feb 19, 2024
8f19cce
test_remove_entity_registry
bdraco Feb 19, 2024
4b3d429
rewrite
bdraco Feb 19, 2024
c2db3c5
Fix race in removing entities from the registry
bdraco Feb 19, 2024
40bb0ed
Fix pytest assertions from tessie test common
bdraco Feb 19, 2024
af3d8ef
does not fail locally
bdraco Feb 20, 2024
3e0b407
Merge remote-tracking branch 'upstream/dev' into tessie_fix_assertions
bdraco Feb 20, 2024
3df9c14
fix snapshots
bdraco Feb 20, 2024
672c4af
Merge branch 'tessie_fix_assertions' into entity_remove_race
bdraco Feb 20, 2024
5c6a961
Merge branch 'entity_remove_race' into entity_removal_never_normally_…
bdraco Feb 20, 2024
3f884a5
revert testing
bdraco Feb 20, 2024
ff776cc
Merge branch 'dev' into entity_removal_never_normally_suspends
bdraco Feb 20, 2024
cfdc1da
Merge branch 'dev' into entity_remove_race
bdraco Feb 20, 2024
0e884d4
fix race
bdraco Feb 20, 2024
9b0b3da
fix race
bdraco Feb 20, 2024
a928041
fully fixed
bdraco Feb 20, 2024
cde00c2
Merge remote-tracking branch 'upstream/dev' into entity_remove_race
bdraco Feb 20, 2024
e82f177
Merge branch 'entity_remove_race' into entity_removal_never_normally_…
bdraco Feb 20, 2024
d796aa8
Merge branch 'dev' into entity_removal_never_normally_suspends
bdraco Feb 21, 2024
1a0eec7
Merge branch 'dev' into entity_removal_never_normally_suspends
bdraco Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions homeassistant/helpers/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ class Entity(

__capabilities_updated_at: deque[float]
__capabilities_updated_at_reported: bool = False
__remove_event: asyncio.Event | None = None
__remove_future: asyncio.Future[None] | None = None

# Entity Properties
_attr_assumed_state: bool = False
Expand Down Expand Up @@ -1338,15 +1338,18 @@ async def async_remove(self, *, force_remove: bool = False) -> None:
If the entity doesn't have a non disabled entry in the entity registry,
or if force_remove=True, its state will be removed.
"""
if self.__remove_event is not None:
await self.__remove_event.wait()
if self.__remove_future is not None:
await self.__remove_future
return

self.__remove_event = asyncio.Event()
self.__remove_future = self.hass.loop.create_future()
try:
await self.__async_remove_impl(force_remove)
except BaseException as ex:
self.__remove_future.set_exception(ex)
raise
finally:
self.__remove_event.set()
self.__remove_future.set_result(None)

@final
async def __async_remove_impl(self, force_remove: bool) -> None:
Expand Down Expand Up @@ -1496,8 +1499,8 @@ async def _async_process_registry_update_or_remove(

self.entity_id = registry_entry.entity_id

# Clear the remove event to handle entity added again after entity id change
self.__remove_event = None
# Clear the remove future to handle entity added again after entity id change
self.__remove_future = None
self._platform_state = EntityPlatformState.NOT_ADDED
await self.platform.async_add_entities([self])

Expand Down
14 changes: 11 additions & 3 deletions homeassistant/helpers/entity_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,9 +792,17 @@ async def async_reset(self) -> None:
if not self.entities:
return

tasks = [entity.async_remove() for entity in self.entities.values()]

await asyncio.gather(*tasks)
# Removals are awaited in series since in most
# cases calling async_remove will not yield control
# to the event loop and we want to avoid scheduling
# one task per entity.
for entity in list(self.entities.values()):
try:
await entity.async_remove()
except Exception: # pylint: disable=broad-except
self.logger.exception(
"Error while removing entity %s", entity.entity_id
)

self.async_unsub_polling()
self._setup_complete = False
Expand Down