Files
ThordekkCore/DEADLOCK_ROOT_CAUSE_ANALYSIS.md
T

131 lines
5.1 KiB
Markdown
Raw Normal View History

2026-01-20 21:28:45 -03:00
# DEADLOCK ROOT CAUSE ANALYSIS - THE ACTUAL SOURCE
## CRITICAL DISCOVERY: The Deadlock is NOT in BotAI.cpp
After 12 deadlock fixes focused on BotAI.cpp, the deadlock persists because **we've been looking in the wrong place**.
## THE ACTUAL DEADLOCK SOURCE
**Location**: `GroupInvitationHandler.cpp`
**Lines**: 499-507
**Method**: `SendAcceptPacket()`
### The Deadly Lock Acquisition Chain
Thread 1 (World Update Thread) acquires locks in this order:
1. BotSessionMgr::UpdateAllSessions() - holds _sessionsMutex
2. BotAI::UpdateAI() - acquires shared_lock on BotAI::_mutex (in UpdateStrategies)
3. GroupInvitationHandler::Update() - called while BotAI::_mutex held
4. SendAcceptPacket() - tries botAI->ActivateStrategy("follow")
5. BotAI::ActivateStrategy() - tries to acquire UNIQUE_LOCK on _mutex
6. **DEADLOCK**: Can't upgrade shared_lock to unique_lock on same thread
## DETAILED ANALYSIS
### The Call Stack At Deadlock:
```
BotAI::UpdateAI() [Holds: shared_lock on BotAI::_mutex]
└─ UpdateStrategies(diff) [Acquires shared_lock - OK]
└─ _groupInvitationHandler->Update(diff) [Line 157 - still holds shared_lock]
└─ ProcessNextInvitation()
└─ AcceptInvitationInternal()
└─ SendAcceptPacket()
└─ Line 499: botAI->GetStrategy("follow") [Tries shared_lock AGAIN]
└─ Line 505: botAI->ActivateStrategy("follow") [Tries UNIQUE_LOCK - DEADLOCK]
└─ BotAI::ActivateStrategy()
└─ std::unique_lock lock(_mutex) [BLOCKED: can't upgrade shared to unique]
```
### Why This Deadlocks:
1. **BotAI::UpdateStrategies()** acquires `std::shared_lock` on `BotAI::_mutex`
2. Still holding that lock, **BotAI::UpdateAI()** calls `GroupInvitationHandler::Update()`
3. GroupInvitationHandler processes group join, calls `SendAcceptPacket()`
4. SendAcceptPacket() tries to call **BotAI::ActivateStrategy()** (line 505)
5. ActivateStrategy() needs `std::unique_lock` on same `BotAI::_mutex`
6. **DEADLOCK**: Same thread cannot upgrade shared_lock to unique_lock
### Root Cause:
**Reentrant lock acquisition from callback context**
The GroupInvitationHandler is called FROM BotAI::UpdateAI() while locks are held, then tries to call BACK into BotAI methods that need stronger locks.
## THE SOLUTION
### IMMEDIATE FIX (Remove Redundant Code):
**File**: `c:/TrinityBots/TrinityCore/src/modules/Playerbot/Group/GroupInvitationHandler.cpp`
**Action**: DELETE lines 497-507
```cpp
// DELETE THESE LINES (they cause the deadlock):
// BACKUP FIX: Directly activate follow strategy as fallback
TC_LOG_INFO("module.playerbot.group", "Bot directly activating follow strategy", _bot->GetName());
if (botAI->GetStrategy("follow")) // ← Line 499: REENTRANT LOCK
{
TC_LOG_INFO("module.playerbot.group", "Bot already has follow strategy", _bot->GetName());
}
else
{
botAI->ActivateStrategy("follow"); // ← Line 505: DEADLOCK SOURCE
TC_LOG_INFO("module.playerbot.group", "Bot activated follow strategy", _bot->GetName());
}
```
**Reason**: OnGroupJoined() is already called at lines 455 and 494, which properly activates the follow strategy. Lines 497-507 are redundant backup code that creates the deadlock.
### WHY PREVIOUS FIXES DIDN'T WORK
All 12 fixes focused on **BotAI::UpdateStrategies()** lock ordering, but that's not the problem:
- UpdateStrategies() correctly acquires and releases locks
- The issue is in **GroupInvitationHandler::SendAcceptPacket()** calling back into BotAI
- The callback happens WHILE BotAI::_mutex is still held (shared)
- Then tries to acquire unique_lock on the SAME mutex = deadlock
## VALIDATION
After fix, test:
1. Create bot
2. Invite bot to group
3. Bot auto-accepts
4. Bot starts following
5. **NO "resource deadlock would occur" exception**
## THREADING MODEL
### Single Main Thread:
```
Main World Thread:
└─> PlayerbotModule::OnUpdate(diff)
└─> BotSessionMgr::UpdateAllSessions(diff)
└─> For each session (SEQUENTIAL):
└─> BotSession::Update(diff)
└─> BotAI::UpdateAI(diff)
├─ UpdateStrategies() [shared_lock]
└─ GroupInvitationHandler::Update() [CALLBACK - DEADLOCK HERE]
```
**Key**: Single-threaded, but deadlock happens from reentrant callback, not threading.
## COMPREHENSIVE MUTEX INVENTORY
**Total**: 200+ mutexes across 150+ classes in Playerbot module
**High-Risk** (Direct BotAI interaction):
- GroupInvitationHandler::_invitationMutex
- BotSession::_packetMutex
- BotSessionMgr::_sessionsMutex
- ActionPriority::_queueMutex, _poolMutex
- CooldownManager::_cooldownMutex, _categoryMutex
**Medium-Risk** (Indirect):
- All ClassAI specializations (200+ _cooldownMutex)
- Combat managers
- Performance monitors
## CONCLUSION
The deadlock source was **GroupInvitationHandler.cpp lines 497-507**, not BotAI.cpp.
**Immediate action**: Delete the redundant backup code that tries to activate strategy from callback context.