11 KiB
COMPLETE DEADLOCK AUDIT - All Map Access Points from Worker Threads
EXECUTION FLOW CONFIRMATION
UpdateAI (line 314) → UpdateManagers (line 502) → ALL MANAGER UPDATE CALLS
This means ALL managers listed below run on WORKER THREADS from the thread pool!
CRITICAL FINDINGS
Total ObjectAccessor Calls from Worker Threads: 31 calls
| System | Files | ObjectAccessor Calls | Severity |
|---|---|---|---|
| TargetScanner | 1 | ✅ FIXED (was 3) | CRITICAL (was main bottleneck) |
| ClassAI | 2 | ✅ PARTIALLY FIXED (5 remaining in helpers) | HIGH |
| QuestManager | 3 | ❌ 11 calls | CRITICAL |
| GatheringManager | 1 | ❌ 5 calls | CRITICAL |
| Strategy Systems | 3 | ❌ 10 calls | HIGH |
1. TargetScanner (FIXED ✅)
File: src/modules/Playerbot/AI/Combat/TargetScanner.cpp
Status: COMPLETELY FIXED - Returns GUIDs, BotAI resolves on main thread
Previous Calls:
- Line 328:
ObjectAccessor::GetUnit()in FindAllHostiles() - ELIMINATED - Line 183-264: FindBestTarget() - ELIMINATED
- Line 128-181: FindNearestHostile() - ELIMINATED
Impact: MASSIVE - Called hundreds of times per second across thousands of bots
2. ClassAI Systems (PARTIALLY FIXED ⚠️)
UnholySpecialization.cpp (5 errors remaining)
File: src/modules/Playerbot/AI/ClassAI/DeathKnights/UnholySpecialization.cpp
Status: Main methods fixed, helper methods still broken
Fixed:
- ✅ Line 1555: UpdateGhoulManagement() - Now accepts target parameter
- ✅ Line 1603: CommandGhoulIfNeeded() - Uses target parameter
Remaining Errors (need target parameter passed through):
- ❌ Line 1085: HandleEmergencySurvival() - Uses
targetwithout parameter - ❌ Line 1182: UpdateCombatPhase() - Uses
targetwithout parameter - ❌ Line 1185: UpdateCombatPhase() - Uses
targetwithout parameter - ❌ Line 1212: UpdateCombatPhase() - Uses
targetwithout parameter (2x)
EvokerAI.cpp (FIXED ✅)
File: src/modules/Playerbot/AI/ClassAI/Evokers/EvokerAI.cpp
Status: COMPLETELY FIXED
- ✅ Line 656: UpdateEssenceManagement() - Now accepts target parameter
3. QuestManager Systems (NOT FIXED ❌)
Execution Context: Called from BotAI::UpdateAI() → UpdateManagers() line 1826
Runs On: WORKER THREADS via thread pool
Severity: CRITICAL - Quest operations are frequent
QuestCompletion.cpp (6 calls)
File: src/modules/Playerbot/Quest/QuestCompletion.cpp
Line 467: Creature* creature = ObjectAccessor::GetCreature(*bot, guid);
Line 577: Creature* creature = ObjectAccessor::GetCreature(*bot, guid);
Line 827: auto* entity = ObjectAccessor::GetCreature(*bot, guid);
Line 902: auto* entity = ObjectAccessor::GetCreature(*bot, guid);
Line 1003: auto* entity = ObjectAccessor::GetCreature(*bot, guid);
Line 1063: Creature* creature = ObjectAccessor::GetCreature(*bot, guid);
Context: Quest completion checks, turn-in validation, NPC interaction
QuestPickup.cpp (3 calls)
File: src/modules/Playerbot/Quest/QuestPickup.cpp
Line 242: Creature* creature = ObjectAccessor::GetCreature(*bot, ObjectGuid::Create<...>);
Line 248: GameObject* go = ObjectAccessor::GetGameObject(*bot, ObjectGuid::Create<...>);
Line 406: auto* entity = ObjectAccessor::GetCreature(*bot, guid);
Context: Quest giver detection, quest acceptance
QuestTurnIn.cpp (2 calls)
File: src/modules/Playerbot/Quest/QuestTurnIn.cpp
Line 400: Creature* creature = ObjectAccessor::GetCreature(*bot, guid);
Context: Quest turn-in NPC validation
4. GatheringManager (NOT FIXED ❌)
Execution Context: Called from BotAI::UpdateAI() → UpdateManagers() line 1842
Runs On: WORKER THREADS via thread pool
Severity: CRITICAL - Gathering operations are very frequent
GatheringManager.cpp (5 calls)
File: src/modules/Playerbot/Professions/GatheringManager.cpp
Line 242: target = ObjectAccessor::GetCreature(*GetBot(), node.guid);
// In PerformGathering() - Skinning corpses
Line 260: GameObject* gameObject = ObjectAccessor::GetGameObject(*GetBot(), node.guid);
// In PerformGathering() - Mining/Herbalism nodes
Line 604: Creature* creature = ObjectAccessor::GetCreature(*GetBot(), guid);
// In ScanForSkinnableCorpses() - Finding skinnable mobs
Line 665: Creature* creature = ObjectAccessor::GetCreature(*GetBot(), node.guid);
// In IsNodeValid() - Validating skinnable corpses
Line 670: GameObject* gameObject = ObjectAccessor::GetGameObject(*GetBot(), node.guid);
// In IsNodeValid() - Validating gathering nodes
Context: Mining, Herbalism, Skinning - All professions that scan/interact with world objects
5. Strategy Systems (NOT FIXED ❌)
Execution Context: Strategies are executed during BotAI::UpdateAI() trigger processing Runs On: WORKER THREADS via thread pool Severity: HIGH - Combat/movement strategies run frequently
CombatMovementStrategy.cpp (1 call)
File: src/modules/Playerbot/AI/Strategy/CombatMovementStrategy.cpp
Line 417: Creature* creature = ObjectAccessor::GetCreature(*player, guid);
Context: Combat positioning, kiting, range management
LootStrategy.cpp (6 calls)
File: src/modules/Playerbot/AI/Strategy/LootStrategy.cpp
Line 286: Creature* creature = ObjectAccessor::GetCreature(*bot, corpseGuid);
// Finding lootable corpses
Line 324: GameObject* object = ObjectAccessor::GetGameObject(*bot, objectGuid);
// Finding lootable containers
Line 394: objA = ObjectAccessor::GetCreature(*bot, a);
// Loot priority comparison
Line 396: objA = ObjectAccessor::GetGameObject(*bot, a);
// Loot priority comparison
Line 399: objB = ObjectAccessor::GetCreature(*bot, b);
// Loot priority comparison
Line 401: objB = ObjectAccessor::GetGameObject(*bot, b);
// Loot priority comparison
Context: Loot scanning, priority calculation, corpse/container detection
QuestStrategy.cpp (3 calls)
File: src/modules/Playerbot/AI/Strategy/QuestStrategy.cpp
Line 875: GameObject* go = ObjectAccessor::GetGameObject(*bot, ObjectGuid::Create<...>);
// Quest objective interaction
Line 1375: ::Unit* target = ObjectAccessor::GetUnit(*bot, ObjectGuid::Create<...>);
// Quest target validation
Line 1456: GameObject* gameObject = ObjectAccessor::GetGameObject(*bot, ObjectGuid::Create<...>);
// Quest object interaction
Context: Quest objective tracking, interaction logic
DEADLOCK MECHANISM
How These Cause Deadlocks
- BotAI::UpdateAI() runs on worker threads from thread pool
- UpdateManagers() calls QuestManager, GatheringManager, etc. on worker threads
- Strategy execution happens during trigger processing on worker threads
- All ObjectAccessor::Get* methods access Map::_objectsStore
- Map::_objectsStore is an unprotected std::unordered_map (NO MUTEXES)
- Multiple worker threads accessing the same unprotected data structure → DEADLOCK
Why Futures 3-14 Never Complete
With 5000+ bots:
- Thousands of worker threads executing UpdateAI() simultaneously
- Each thread calling QuestManager, GatheringManager, and Strategies
- Each manager making 1-11 ObjectAccessor calls
- Total concurrent Map access: 5000 bots × ~20 calls = ~100,000 concurrent Map accesses
- Map has NO protection → undefined behavior → futures hang forever
FIX STRATEGY
Pattern (Based on Successful TargetScanner Fix)
BEFORE (DEADLOCK):
void SomeManager::Update(uint32 diff)
{
// Running on worker thread!
Creature* npc = ObjectAccessor::GetCreature(*bot, guid); // DEADLOCK!
if (npc)
DoSomething(npc);
}
AFTER (THREAD-SAFE):
void SomeManager::Update(uint32 diff)
{
// Running on worker thread!
ObjectGuid npcGuid = FindNPC(); // Use spatial grid snapshots
if (!npcGuid.IsEmpty())
{
// Queue action with GUID - main thread will resolve
sBotActionMgr->QueueAction(BotAction::InteractNPC(
bot->GetGUID(),
npcGuid,
getMSTime()
));
}
}
Implementation Steps
-
QuestManager Systems (11 calls):
- Change methods to work with GUIDs from spatial grid
- Queue ACCEPT_QUEST/TURN_IN_QUEST/INTERACT_NPC actions
- Main thread resolves GUIDs → pointers safely
-
GatheringManager (5 calls):
- Change PerformGathering to accept GUID parameter
- Change ScanForSkinnableCorpses to return GUIDs
- Change IsNodeValid to accept GUID parameter
- Queue INTERACT_OBJECT/LOOT_OBJECT actions
-
Strategy Systems (10 calls):
- CombatMovementStrategy: Use position data from snapshots
- LootStrategy: Return GUIDs, queue LOOT_OBJECT actions
- QuestStrategy: Queue INTERACT_OBJECT/INTERACT_NPC actions
-
UnholySpecialization Helper Methods (5 remaining):
- Pass target parameter through entire call chain
- HandleEmergencySurvival(::Unit* target)
- UpdateCombatPhase(::Unit* target)
PRIORITY ORDER
IMMEDIATE (Block Compilation):
- ✅ Fix UnholySpecialization compilation errors (5 errors)
CRITICAL (High Frequency):
- ❌ Fix GatheringManager (5 calls) - Runs every frame for gathering bots
- ❌ Fix QuestManager (11 calls) - Runs every frame for questing bots
HIGH (Frequent):
- ❌ Fix LootStrategy (6 calls) - Runs after every combat
- ❌ Fix QuestStrategy (3 calls) - Runs during quest execution
MEDIUM:
- ❌ Fix CombatMovementStrategy (1 call) - Combat positioning
EXPECTED RESULTS AFTER COMPLETE FIX
With ALL 31 ObjectAccessor Calls Eliminated:
✅ Zero Map access from worker threads ✅ 100% lock-free spatial grid queries ✅ All GUID resolution on main thread only ✅ Action queue handles all Map-touching operations
Testing Targets:
- Build succeeds (fix UnholySpecialization errors first)
- Spawn 100 bots - verify futures complete
- Spawn 1000 bots - verify futures complete
- Spawn 5000 bots - CRITICAL TEST: futures 3-14 MUST complete
- Monitor for 60-second hangs - MUST BE ZERO
- Verify quest operations work
- Verify gathering operations work
- Verify combat rotations work
- Check server logs for deadlock warnings
NEXT ACTIONS
- Fix UnholySpecialization compilation errors (blocking build)
- Create automated refactoring script for all 31 ObjectAccessor calls
- Implement fixes system-by-system following TargetScanner pattern
- Build and test with increasing bot counts (100 → 1000 → 5000)
- Verify futures 3-14 complete with 5000+ bots
SUMMARY
CRITICAL DEADLOCK SOURCES IDENTIFIED: 31 ObjectAccessor calls from worker threads
SYSTEMS AFFECTED: Quest (11), Gathering (5), Loot (6), ClassAI (5), Quest Strategy (3), Combat Movement (1)
ROOT CAUSE: All managers run on worker threads via BotAI::UpdateAI() → UpdateManagers()
FIX PATTERN: Return GUIDs instead of pointers, queue actions, resolve on main thread
EXPECTED OUTCOME: 100% deadlock elimination, futures 3-14 complete with 5000+ bots