Skip to content

Commit

Permalink
fix(api): clear ChangeNotifier's internal event immediately after wai…
Browse files Browse the repository at this point in the history
…ting

After the event flag is set(), the waiter no longer waits. If the event flag is not cleared
immediately, this creates a potential scenario in which the notify() needs to set the event flag
again before the waiter gets around to clearing it. This causes some events to be dropped. Simply
clear the event flag immediately after waiting.
  • Loading branch information
mjhuff committed May 6, 2024
1 parent 4fe67a9 commit 57ff88b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
2 changes: 1 addition & 1 deletion api/src/opentrons/util/change_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def notify(self) -> None:

async def wait(self) -> None:
"""Wait until the next state change notification."""
self._event.clear()
await self._event.wait()
self._event.clear()


class ChangeNotifier_ts(ChangeNotifier):
Expand Down
27 changes: 27 additions & 0 deletions api/tests/opentrons/util/test_change_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,30 @@ async def _do_task_3() -> None:
await asyncio.gather(task_1, task_2, task_3)

assert results == [1, 2, 3]


async def test_notify_while_busy() -> None:
"""Test that waiters process a new notify() after they are done being busy."""
subject = ChangeNotifier()
results = []

async def _do_task() -> None:
results.append("TEST")
await asyncio.sleep(0.2) # Simulate being busy

async def do_task() -> None:
while True:
await subject.wait()
await _do_task()

task = asyncio.create_task(do_task())

subject.notify()
await asyncio.sleep(0.0)

subject.notify()
await asyncio.sleep(0.5)

assert results == ["TEST", "TEST"]

task.cancel()

0 comments on commit 57ff88b

Please sign in to comment.