Fix shutdown deadlock with run_callback_threadsafe (#45807)

This commit is contained in:
J. Nick Koston 2021-01-31 23:54:39 -10:00 committed by GitHub
parent 31a84555b9
commit 9f59515bb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 2 deletions

View file

@ -50,7 +50,8 @@ def test_fire_coroutine_threadsafe_from_inside_event_loop(
def test_run_callback_threadsafe_from_inside_event_loop(mock_ident, _):
"""Testing calling run_callback_threadsafe from inside an event loop."""
callback = MagicMock()
loop = MagicMock()
loop = Mock(spec=["call_soon_threadsafe"])
loop._thread_ident = None
mock_ident.return_value = 5
@ -168,3 +169,45 @@ async def test_gather_with_concurrency():
)
assert results == [2, 2, -1, -1]
async def test_shutdown_run_callback_threadsafe(hass):
"""Test we can shutdown run_callback_threadsafe."""
hasync.shutdown_run_callback_threadsafe(hass.loop)
callback = MagicMock()
with pytest.raises(RuntimeError):
hasync.run_callback_threadsafe(hass.loop, callback)
async def test_run_callback_threadsafe(hass):
"""Test run_callback_threadsafe runs code in the event loop."""
it_ran = False
def callback():
nonlocal it_ran
it_ran = True
assert hasync.run_callback_threadsafe(hass.loop, callback)
assert it_ran is False
# Verify that async_block_till_done will flush
# out the callback
await hass.async_block_till_done()
assert it_ran is True
async def test_callback_is_always_scheduled(hass):
"""Test run_callback_threadsafe always calls call_soon_threadsafe before checking for shutdown."""
# We have to check the shutdown state AFTER the callback is scheduled otherwise
# the function could continue on and the caller call `future.result()` after
# the point in the main thread where callbacks are no longer run.
callback = MagicMock()
hasync.shutdown_run_callback_threadsafe(hass.loop)
with patch.object(hass.loop, "call_soon_threadsafe") as mock_call_soon_threadsafe:
with pytest.raises(RuntimeError):
hasync.run_callback_threadsafe(hass.loop, callback)
mock_call_soon_threadsafe.assert_called_once()