fix(provider_state): change Lock to RLock to prevent re-entrant deadlock
TIER-3 READ AGENTS.md + conductor/code_styleguides/error_handling.md + src/provider_state.py + src/ai_client.py:2148-2220 before provider-state-rlock-fix.
Tier 2's 25a22057 commit re-bound the 14 module globals in src/ai_client.py as
aliases to provider_state.get_history(...) instances. The ProviderHistory dunder
methods (__bool__, __len__, __iter__, __getitem__) all use \with self.lock:\.
The dunders are non-reentrant: \ hreading.Lock\ blocks if the lock is already
held. The call site in src/ai_client.py:2210-2217 acquires the lock via
\with _deepseek_history_lock:\ (alias to ProviderHistory.lock), then calls
_rerepair_deepseek_history(_deepseek_history) which does \history[-1]\
(acquires the lock again -> DEADLOCK). This caused
tests/test_deepseek_provider.py::test_deepseek_completion_logic to hang
with a 30s timeout.
Fix: change \ hreading.Lock\ to \ hreading.RLock\ in ProviderHistory.
The dunders can now be safely called while the lock is already held.
Also removed:
- Duplicate @dataclass decorator on ProviderHistory (line 25-26)
- Duplicate _PROVIDER_HISTORIES dict declaration (lines 64-71 and 74-81)
Acceptance: test_deepseek_provider (7/7) + test_provider_state + test_ai_client_result + test_ai_client_tool_loop all pass.
This commit is contained in:
+1
-12
@@ -22,11 +22,10 @@ from dataclasses import dataclass, field
|
||||
from src.type_aliases import HistoryMessage, Metadata
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass
|
||||
class ProviderHistory:
|
||||
messages: list[HistoryMessage] = field(default_factory=list)
|
||||
lock: threading.Lock = field(default_factory=threading.Lock)
|
||||
lock: threading.RLock = field(default_factory=threading.RLock)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
with self.lock:
|
||||
@@ -71,16 +70,6 @@ _PROVIDER_HISTORIES: dict[str, ProviderHistory] = {
|
||||
}
|
||||
|
||||
|
||||
_PROVIDER_HISTORIES: dict[str, ProviderHistory] = {
|
||||
"anthropic": ProviderHistory(),
|
||||
"deepseek": ProviderHistory(),
|
||||
"minimax": ProviderHistory(),
|
||||
"qwen": ProviderHistory(),
|
||||
"grok": ProviderHistory(),
|
||||
"llama": ProviderHistory(),
|
||||
}
|
||||
|
||||
|
||||
def get_history(provider: str) -> ProviderHistory:
|
||||
if provider not in _PROVIDER_HISTORIES:
|
||||
raise KeyError(f"Unknown provider: {provider!r}")
|
||||
|
||||
Reference in New Issue
Block a user