5.1 KiB
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:
- BotSessionMgr::UpdateAllSessions() - holds _sessionsMutex
- BotAI::UpdateAI() - acquires shared_lock on BotAI::_mutex (in UpdateStrategies)
- GroupInvitationHandler::Update() - called while BotAI::_mutex held
- SendAcceptPacket() - tries botAI->ActivateStrategy("follow")
- BotAI::ActivateStrategy() - tries to acquire UNIQUE_LOCK on _mutex
- 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:
- BotAI::UpdateStrategies() acquires
std::shared_lockonBotAI::_mutex - Still holding that lock, BotAI::UpdateAI() calls
GroupInvitationHandler::Update() - GroupInvitationHandler processes group join, calls
SendAcceptPacket() - SendAcceptPacket() tries to call BotAI::ActivateStrategy() (line 505)
- ActivateStrategy() needs
std::unique_lockon sameBotAI::_mutex - 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
// 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:
- Create bot
- Invite bot to group
- Bot auto-accepts
- Bot starts following
- 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.