mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Fixes crashes and race conditions when bots perform group/guild/arena operations by moving thread-unsafe code to world thread. Potentially fixes #1124 ## Changes - Added operation queue system that runs in world thread - Group operations (invite, remove, convert to raid, set leader) now queued - Arena formation refactored to use queue - Guild operations changed to use packet queueing ## Testing Set `MapUpdate.Threads` > 1 in worldserver.conf to enable multiple map threads, then test: - Group formation and disbanding - Arena team formation - Guild operations (invite, promote, demote, remove) - Run with TSAN cmake ../ \ -DCMAKE_CXX_FLAGS="-fsanitize=thread -g -O1" \ -DCMAKE_C_FLAGS="-fsanitize=thread -g -O1" \ -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \ -DCMAKE_INSTALL_PREFIX=/path/to/install \ -DCMAKE_BUILD_TYPE=RelWithDebInfo build export TSAN_OPTIONS="log_path=tsan_report:halt_on_error=0:second_deadlock_stack=1" ./worldserver The crashes/race conditions should no longer occur with concurrent map threads. ## New Files - `PlayerbotOperation.h` - Base class defining the operation interface (Execute, IsValid, GetPriority) - `PlayerbotOperations.h` - Concrete implementations: GroupInviteOperation, GroupRemoveMemberOperation, GroupConvertToRaidOperation, GroupSetLeaderOperation, ArenaGroupFormationOperation - `PlayerbotWorldThreadProcessor.h/cpp` - Singleton processor with mutex-protected queue, processes operations in WorldScript::OnUpdate hook, handles batch processing and validation --------- Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: SaW <swerkhoven@outlook.com> Co-authored-by: bash <hermensb@gmail.com>
143 lines
4.2 KiB
C++
143 lines
4.2 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#ifndef _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
|
|
#define _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
|
|
|
|
#include "Common.h"
|
|
#include "PlayerbotOperation.h"
|
|
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
|
|
/**
|
|
* @brief Processes thread-unsafe bot operations in the world thread
|
|
*
|
|
* The PlayerbotWorldThreadProcessor manages a queue of operations that must be executed
|
|
* in the world thread rather than map threads. This ensures thread safety for operations
|
|
* like group modifications, LFG, guilds, battlegrounds, etc.
|
|
*
|
|
* Architecture:
|
|
* - Map threads queue operations via QueueOperation()
|
|
* - World thread processes operations via Update() (called from WorldScript::OnUpdate)
|
|
* - Operations are processed in priority order
|
|
* - Thread-safe queue protected by mutex
|
|
*
|
|
* Usage:
|
|
* auto op = std::make_unique<MyOperation>(botGuid, params);
|
|
* sPlayerbotWorldProcessor->QueueOperation(std::move(op));
|
|
*/
|
|
class PlayerbotWorldThreadProcessor
|
|
{
|
|
public:
|
|
PlayerbotWorldThreadProcessor();
|
|
~PlayerbotWorldThreadProcessor();
|
|
|
|
static PlayerbotWorldThreadProcessor* instance();
|
|
|
|
/**
|
|
* @brief Update and process queued operations (called from world thread)
|
|
*
|
|
* This method should be called from WorldScript::OnUpdate hook, which runs in the world thread.
|
|
* It processes a batch of queued operations.
|
|
*
|
|
* @param diff Time since last update in milliseconds
|
|
*/
|
|
void Update(uint32 diff);
|
|
|
|
/**
|
|
* @brief Queue an operation for execution in the world thread
|
|
*
|
|
* Thread-safe method that can be called from any thread (typically map threads).
|
|
* The operation will be executed later during Update().
|
|
*
|
|
* @param operation Unique pointer to the operation (ownership is transferred)
|
|
* @return true if operation was queued, false if queue is full
|
|
*/
|
|
bool QueueOperation(std::unique_ptr<PlayerbotOperation> operation);
|
|
|
|
/**
|
|
* @brief Get current queue size
|
|
*
|
|
* Thread-safe method for monitoring queue size.
|
|
*
|
|
* @return Number of operations waiting to be processed
|
|
*/
|
|
uint32 GetQueueSize() const;
|
|
|
|
/**
|
|
* @brief Clear all queued operations
|
|
*
|
|
* Used during shutdown or emergency situations.
|
|
*/
|
|
void ClearQueue();
|
|
|
|
/**
|
|
* @brief Get statistics about operation processing
|
|
*/
|
|
struct Statistics
|
|
{
|
|
uint64 totalOperationsProcessed = 0;
|
|
uint64 totalOperationsFailed = 0;
|
|
uint64 totalOperationsSkipped = 0;
|
|
uint32 currentQueueSize = 0;
|
|
uint32 maxQueueSize = 0;
|
|
uint32 averageExecutionTimeMs = 0;
|
|
};
|
|
|
|
Statistics GetStatistics() const;
|
|
|
|
/**
|
|
* @brief Enable/disable operation processing
|
|
*
|
|
* When disabled, operations are still queued but not processed.
|
|
* Useful for testing or temporary suspension.
|
|
*
|
|
* @param enabled true to enable processing, false to disable
|
|
*/
|
|
void SetEnabled(bool enabled) { m_enabled = enabled; }
|
|
|
|
bool IsEnabled() const { return m_enabled; }
|
|
|
|
private:
|
|
/**
|
|
* @brief Process a single batch of operations
|
|
*
|
|
* Extracts operations from queue and executes them.
|
|
* Called internally by Update().
|
|
*/
|
|
void ProcessBatch();
|
|
|
|
/**
|
|
* @brief Check if queue is approaching capacity
|
|
*
|
|
* Logs warning if queue is getting full.
|
|
*/
|
|
void CheckQueueHealth();
|
|
|
|
// Thread-safe queue
|
|
mutable std::mutex m_queueMutex;
|
|
std::queue<std::unique_ptr<PlayerbotOperation>> m_operationQueue;
|
|
|
|
// Configuration
|
|
bool m_enabled;
|
|
uint32 m_maxQueueSize; // Maximum operations in queue
|
|
uint32 m_batchSize; // Operations to process per Update()
|
|
uint32 m_queueWarningThreshold; // Warn when queue reaches this percentage
|
|
|
|
// Statistics
|
|
mutable std::mutex m_statsMutex;
|
|
Statistics m_stats;
|
|
|
|
// Timing
|
|
uint32 m_timeSinceLastUpdate;
|
|
uint32 m_updateInterval; // Minimum ms between updates
|
|
};
|
|
|
|
#define sPlayerbotWorldProcessor PlayerbotWorldThreadProcessor::instance()
|
|
|
|
#endif
|