Fix state overwrite race condition where two platforms request the same entity_id (#42151)

* Fix state overwrite race condition where two platforms request the same entity id

* fix test

* create reservations instead

* revert

* cannot use __slots__ because we patch async_all
This commit is contained in:
J. Nick Koston 2020-10-21 10:01:51 -05:00 committed by GitHub
parent bb641c23a9
commit df2ede6522
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 119 additions and 8 deletions

View file

@ -973,6 +973,7 @@ class StateMachine:
def __init__(self, bus: EventBus, loop: asyncio.events.AbstractEventLoop) -> None:
"""Initialize state machine."""
self._states: Dict[str, State] = {}
self._reservations: Set[str] = set()
self._bus = bus
self._loop = loop
@ -1080,6 +1081,9 @@ class StateMachine:
entity_id = entity_id.lower()
old_state = self._states.pop(entity_id, None)
if entity_id in self._reservations:
self._reservations.remove(entity_id)
if old_state is None:
return False
@ -1116,6 +1120,29 @@ class StateMachine:
context,
).result()
@callback
def async_reserve(self, entity_id: str) -> None:
"""Reserve a state in the state machine for an entity being added.
This must not fire an event when the state is reserved.
This avoids a race condition where multiple entities with the same
entity_id are added.
"""
entity_id = entity_id.lower()
if entity_id in self._states or entity_id in self._reservations:
raise HomeAssistantError(
"async_reserve must not be called once the state is in the state machine."
)
self._reservations.add(entity_id)
@callback
def async_available(self, entity_id: str) -> bool:
"""Check to see if an entity_id is available to be used."""
entity_id = entity_id.lower()
return entity_id not in self._states and entity_id not in self._reservations
@callback
def async_set(
self,