Fix deadlock in lazy init: _ensure_initialized calling login via request()

login() called client.request() which called _ensure_initialized() which
tried to re-acquire _init_lock — deadlocking forever. Fix: set
_initializing=True while inside the init critical section so request()
skips the _ensure_initialized() guard when called from within init
(safe because query_api_info() already populated the API cache).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 15:49:26 +02:00
parent 737c816ee7
commit e13324e10c
+22 -14
View File
@@ -101,6 +101,7 @@ class DsmClient:
self._reauth_lock = asyncio.Lock()
self._init_lock = asyncio.Lock()
self._initialized = False
self._initializing = False # True while inside _ensure_initialized
logger.debug(
"DsmClient: base_url=%s verify_ssl=%s timeout=%d",
self._base_url,
@@ -131,21 +132,25 @@ class DsmClient:
async with self._init_lock:
if self._initialized: # re-check inside lock
return
sys.stderr.write(f"[dsm] Connecting to {self._base_url}...\n")
sys.stderr.flush()
logger.debug("Lazy init: querying API info from %s", self._base_url)
await self.query_api_info()
sys.stderr.write(f"[dsm] API info OK ({len(self._api_cache)} APIs)\n")
sys.stderr.flush()
if self._auth_manager:
sys.stderr.write("[dsm] Authenticating...\n")
self._initializing = True
try:
sys.stderr.write(f"[dsm] Connecting to {self._base_url}...\n")
sys.stderr.flush()
logger.debug("Lazy init: authenticating")
self._sid = await self._auth_manager.login(self)
sys.stderr.write("[dsm] Auth OK\n")
logger.debug("Lazy init: querying API info from %s", self._base_url)
await self.query_api_info()
sys.stderr.write(f"[dsm] API info OK ({len(self._api_cache)} APIs)\n")
sys.stderr.flush()
self._initialized = True
logger.debug("Lazy init complete")
if self._auth_manager:
sys.stderr.write("[dsm] Authenticating...\n")
sys.stderr.flush()
logger.debug("Lazy init: authenticating")
self._sid = await self._auth_manager.login(self)
sys.stderr.write("[dsm] Auth OK\n")
sys.stderr.flush()
self._initialized = True
logger.debug("Lazy init complete")
finally:
self._initializing = False
async def __aenter__(self) -> DsmClient:
logging.getLogger("httpx").setLevel(logging.WARNING)
@@ -235,7 +240,10 @@ class DsmClient:
"""
sys.stderr.write(f"[dsm] request: {api}/{method}\n")
sys.stderr.flush()
await self._ensure_initialized()
# Skip init guard if we are already inside _ensure_initialized (e.g. login call).
# The API cache is populated before login, so the cache is ready at this point.
if not self._initializing:
await self._ensure_initialized()
http = self._get_http()
if api not in self._api_cache: