Skip to content

Commit

Permalink
fix: avoid concurrent refreshes of adapters (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Feb 23, 2024
1 parent 0a942c9 commit d355b17
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/habluetooth/manager.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ cdef class BluetoothManager:
cdef public bint _debug
cdef public bint shutdown
cdef public object _loop
cdef public object _adapter_refresh_future

@cython.locals(stale_seconds=float)
cdef bint _prefer_previous_adv_from_different_source(
Expand Down
24 changes: 20 additions & 4 deletions src/habluetooth/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class BluetoothManager:
"_debug",
"shutdown",
"_loop",
"_adapter_refresh_future",
)

def __init__(
Expand Down Expand Up @@ -147,6 +148,7 @@ def __init__(
self._debug = _LOGGER.isEnabledFor(logging.DEBUG)
self.shutdown = False
self._loop: asyncio.AbstractEventLoop | None = None
self._adapter_refresh_future: asyncio.Future[None] | None = None

@property
def supports_passive_scan(self) -> bool:
Expand Down Expand Up @@ -193,28 +195,42 @@ def async_scanner_by_source(self, source: str) -> BaseHaScanner | None:
"""Return the scanner for a source."""
return self._sources.get(source)

async def _async_refresh_adapters(self) -> None:
"""Refresh the adapters."""
if self._adapter_refresh_future:
await self._adapter_refresh_future
return
if TYPE_CHECKING:
assert self._loop is not None
self._adapter_refresh_future = self._loop.create_future()
try:
await self._bluetooth_adapters.refresh()
self._adapters = self._bluetooth_adapters.adapters
finally:
self._adapter_refresh_future.set_result(None)
self._adapter_refresh_future = None

async def async_get_bluetooth_adapters(
self, cached: bool = True
) -> dict[str, AdapterDetails]:
"""Get bluetooth adapters."""
if not self._adapters or not cached:
if not cached:
await self._bluetooth_adapters.refresh()
await self._async_refresh_adapters()
self._adapters = self._bluetooth_adapters.adapters
return self._adapters

async def async_get_adapter_from_address(self, address: str) -> str | None:
"""Get adapter from address."""
if adapter := self._find_adapter_by_address(address):
return adapter
await self._bluetooth_adapters.refresh()
self._adapters = self._bluetooth_adapters.adapters
await self._async_refresh_adapters()
return self._find_adapter_by_address(address)

async def async_setup(self) -> None:
"""Set up the bluetooth manager."""
self._loop = asyncio.get_running_loop()
await self._bluetooth_adapters.refresh()
await self._async_refresh_adapters()
install_multiple_bleak_catcher()
self.async_setup_unavailable_tracking()

Expand Down

0 comments on commit d355b17

Please sign in to comment.