From f7cc6f66573a46012ac2849ddc066c8eff007bf3 Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Wed, 15 Apr 2026 16:54:54 +0200 Subject: [PATCH] release command semaphore after timeout --- bumble/host.py | 6 ++---- tests/host_test.py | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/bumble/host.py b/bumble/host.py index 46fd546..afee06a 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -692,10 +692,8 @@ class Host(utils.EventEmitter): finally: self.pending_command = None self.pending_response = None - if ( - response is not None - and response.num_hci_command_packets - and self.command_semaphore.locked() + if response is None or ( + response.num_hci_command_packets and self.command_semaphore.locked() ): self.command_semaphore.release() diff --git a/tests/host_test.py b/tests/host_test.py index 02ad617..ff8961f 100644 --- a/tests/host_test.py +++ b/tests/host_test.py @@ -171,14 +171,15 @@ class Source: class Sink: - response: HCI_Event + response: HCI_Event | None - def __init__(self, source: Source, response: HCI_Event) -> None: + def __init__(self, source: Source, response: HCI_Event | None) -> None: self.source = source self.response = response def on_packet(self, packet: bytes) -> None: - self.source.sink.on_packet(bytes(self.response)) + if self.response is not None: + self.source.sink.on_packet(bytes(self.response)) @pytest.mark.asyncio @@ -228,6 +229,23 @@ async def test_send_sync_command() -> None: assert isinstance(response3.return_parameters, HCI_GenericReturnParameters) +@pytest.mark.asyncio +async def test_send_sync_command_timeout() -> None: + source = Source() + sink = Sink(source, None) + + host = Host(source, sink) + host.ready = True + + with pytest.raises(asyncio.TimeoutError): + await host.send_sync_command(HCI_Reset_Command(), response_timeout=0.01) + + # The sending semaphore should have been released, so this should not block + # indefinitely + with pytest.raises(asyncio.TimeoutError): + await host.send_sync_command(hci.HCI_Reset_Command(), response_timeout=0.01) + + @pytest.mark.asyncio async def test_send_async_command() -> None: source = Source()