mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Misc: Cleanup worldserver/bnetserver main() functions to run destructors in predictable order
* world/bnet servers will now fail to startup if listen ports are in use * Restored "Network.OutUBuff" config option lost during boost transition
This commit is contained in:
@@ -78,22 +78,35 @@ char serviceDescription[] = "TrinityCore World of Warcraft emulator world servic
|
||||
int m_ServiceStatus = -1;
|
||||
#endif
|
||||
|
||||
boost::asio::io_service _ioService;
|
||||
boost::asio::deadline_timer _freezeCheckTimer(_ioService);
|
||||
uint32 _worldLoopCounter(0);
|
||||
uint32 _lastChangeMsTime(0);
|
||||
uint32 _maxCoreStuckTimeInMs(0);
|
||||
class FreezeDetector
|
||||
{
|
||||
public:
|
||||
FreezeDetector(boost::asio::io_service& ioService, uint32 maxCoreStuckTime)
|
||||
: _timer(ioService), _worldLoopCounter(0), _lastChangeMsTime(0), _maxCoreStuckTimeInMs(maxCoreStuckTime) { }
|
||||
|
||||
void SignalHandler(const boost::system::error_code& error, int signalNumber);
|
||||
void FreezeDetectorHandler(const boost::system::error_code& error);
|
||||
static void Start(std::shared_ptr<FreezeDetector> const& freezeDetector)
|
||||
{
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(5));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, std::weak_ptr<FreezeDetector>(freezeDetector), std::placeholders::_1));
|
||||
}
|
||||
|
||||
static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error);
|
||||
|
||||
private:
|
||||
boost::asio::deadline_timer _timer;
|
||||
uint32 _worldLoopCounter;
|
||||
uint32 _lastChangeMsTime;
|
||||
uint32 _maxCoreStuckTimeInMs;
|
||||
};
|
||||
|
||||
void SignalHandler(boost::system::error_code const& error, int signalNumber);
|
||||
AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService);
|
||||
bool StartDB();
|
||||
void StopDB();
|
||||
void WorldUpdateLoop();
|
||||
void ClearOnlineAccounts();
|
||||
void ShutdownCLIThread(std::thread* cliThread);
|
||||
void ShutdownThreadPool(std::vector<std::thread>& threadPool);
|
||||
bool LoadRealmInfo();
|
||||
bool LoadRealmInfo(boost::asio::io_service& ioService);
|
||||
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service);
|
||||
|
||||
/// Launch the Trinity server
|
||||
@@ -111,6 +124,8 @@ extern int main(int argc, char** argv)
|
||||
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
std::shared_ptr<void> protobufHandle(nullptr, [](void*) { google::protobuf::ShutdownProtobufLibrary(); });
|
||||
|
||||
#ifdef _WIN32
|
||||
if (configService.compare("install") == 0)
|
||||
return WinServiceInstall() ? 0 : 1;
|
||||
@@ -129,9 +144,11 @@ extern int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<boost::asio::io_service> ioService = std::make_shared<boost::asio::io_service>();
|
||||
|
||||
sLog->RegisterAppender<AppenderDB>();
|
||||
// If logs are supposed to be handled async then we need to pass the io_service into the Log singleton
|
||||
sLog->Initialize(sConfigMgr->GetBoolDefault("Log.Async.Enable", false) ? &_ioService : nullptr);
|
||||
sLog->Initialize(sConfigMgr->GetBoolDefault("Log.Async.Enable", false) ? ioService.get() : nullptr);
|
||||
|
||||
Trinity::Banner::Show("worldserver-daemon",
|
||||
[](char const* text)
|
||||
@@ -148,6 +165,8 @@ extern int main(int argc, char** argv)
|
||||
|
||||
OpenSSLCrypto::threadsSetup();
|
||||
|
||||
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
|
||||
|
||||
// Seed the OpenSSL's PRNG here.
|
||||
// That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
|
||||
BigNumber seed;
|
||||
@@ -167,7 +186,7 @@ extern int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
// Set signal handlers (this must be done before starting io_service threads, because otherwise they would unblock and exit)
|
||||
boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM);
|
||||
boost::asio::signal_set signals(*ioService, SIGINT, SIGTERM);
|
||||
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
|
||||
signals.add(SIGBREAK);
|
||||
#endif
|
||||
@@ -175,63 +194,87 @@ extern int main(int argc, char** argv)
|
||||
|
||||
// Start the Boost based thread pool
|
||||
int numThreads = sConfigMgr->GetIntDefault("ThreadPool", 1);
|
||||
std::vector<std::thread> threadPool;
|
||||
std::shared_ptr<std::vector<std::thread>> threadPool(new std::vector<std::thread>(), [ioService](std::vector<std::thread>* del)
|
||||
{
|
||||
ioService->stop();
|
||||
for (std::thread& thr : *del)
|
||||
thr.join();
|
||||
|
||||
delete del;
|
||||
});
|
||||
|
||||
if (numThreads < 1)
|
||||
numThreads = 1;
|
||||
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
threadPool.push_back(std::thread(boost::bind(&boost::asio::io_service::run, &_ioService)));
|
||||
threadPool->push_back(std::thread([ioService]() { ioService->run(); }));
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.worldserver");
|
||||
|
||||
// Start the databases
|
||||
if (!StartDB())
|
||||
{
|
||||
ShutdownThreadPool(threadPool);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
|
||||
|
||||
// Set server offline (not connectable)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
|
||||
sRealmList->Initialize(_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10));
|
||||
sRealmList->Initialize(*ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10));
|
||||
|
||||
LoadRealmInfo();
|
||||
std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); });
|
||||
|
||||
sMetric->Initialize(realm.Name, _ioService, []()
|
||||
LoadRealmInfo(*ioService);
|
||||
|
||||
sMetric->Initialize(realm.Name, *ioService, []()
|
||||
{
|
||||
TC_METRIC_VALUE("online_players", sWorld->GetPlayerCount());
|
||||
});
|
||||
|
||||
TC_METRIC_EVENT("events", "Worldserver started", "");
|
||||
|
||||
// Initialize the World
|
||||
std::shared_ptr<void> sMetricHandle(nullptr, [](void*)
|
||||
{
|
||||
TC_METRIC_EVENT("events", "Worldserver shutdown", "");
|
||||
sMetric->ForceSend();
|
||||
});
|
||||
|
||||
sScriptMgr->SetScriptLoader(AddScripts);
|
||||
std::shared_ptr<void> sScriptMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sScriptMgr->Unload();
|
||||
sScriptReloadMgr->Unload();
|
||||
});
|
||||
|
||||
// Initialize the World
|
||||
sWorld->SetInitialWorldSettings();
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::thread* cliThread = nullptr;
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true))
|
||||
#endif
|
||||
std::shared_ptr<void> mapManagementHandle(nullptr, [](void*)
|
||||
{
|
||||
cliThread = new std::thread(CliThread);
|
||||
}
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sInstanceSaveMgr->Unload();
|
||||
sOutdoorPvPMgr->Die(); // unload it before MapManager
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
});
|
||||
|
||||
// Start the Remote Access port (acceptor) if enabled
|
||||
AsyncAcceptor* raAcceptor = nullptr;
|
||||
std::unique_ptr<AsyncAcceptor> raAcceptor;
|
||||
if (sConfigMgr->GetBoolDefault("Ra.Enable", false))
|
||||
raAcceptor = StartRaSocketAcceptor(_ioService);
|
||||
raAcceptor.reset(StartRaSocketAcceptor(*ioService));
|
||||
|
||||
// Start soap serving thread if enabled
|
||||
std::thread* soapThread = nullptr;
|
||||
std::shared_ptr<std::thread> soapThread;
|
||||
if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false))
|
||||
{
|
||||
soapThread = new std::thread(TCSoapThread, sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878)));
|
||||
soapThread.reset(new std::thread(TCSoapThread, sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878))),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
}
|
||||
|
||||
// Launch the worldserver listener socket
|
||||
@@ -243,10 +286,36 @@ extern int main(int argc, char** argv)
|
||||
if (networkThreads <= 0)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort, networkThreads);
|
||||
if (!sWorldSocketMgr.StartNetwork(*ioService, worldListener, worldPort, networkThreads))
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Failed to initialize network");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> sWorldSocketMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
sWorldSocketMgr.StopNetwork();
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
});
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::shared_ptr<std::thread> cliThread;
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true))
|
||||
#endif
|
||||
{
|
||||
cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread);
|
||||
}
|
||||
|
||||
// Set server online (allow connecting now)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
@@ -254,11 +323,11 @@ extern int main(int argc, char** argv)
|
||||
realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_OFFLINE));
|
||||
|
||||
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
|
||||
std::shared_ptr<FreezeDetector> freezeDetector;
|
||||
if (int coreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0))
|
||||
{
|
||||
_maxCoreStuckTimeInMs = coreStuckTime * 1000;
|
||||
_freezeCheckTimer.expires_from_now(boost::posix_time::seconds(5));
|
||||
_freezeCheckTimer.async_wait(FreezeDetectorHandler);
|
||||
freezeDetector = std::make_shared<FreezeDetector>(*ioService, coreStuckTime * 1000);
|
||||
freezeDetector->Start(freezeDetector);
|
||||
TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", coreStuckTime);
|
||||
}
|
||||
|
||||
@@ -269,55 +338,17 @@ extern int main(int argc, char** argv)
|
||||
WorldUpdateLoop();
|
||||
|
||||
// Shutdown starts here
|
||||
ShutdownThreadPool(threadPool);
|
||||
threadPool.reset();
|
||||
|
||||
sLog->SetSynchronous();
|
||||
|
||||
sScriptMgr->OnShutdown();
|
||||
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sWorldSocketMgr.StopNetwork();
|
||||
|
||||
sInstanceSaveMgr->Unload();
|
||||
sOutdoorPvPMgr->Die(); // unload it before MapManager
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
sScriptMgr->Unload();
|
||||
sScriptReloadMgr->Unload();
|
||||
|
||||
// set server offline
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
sRealmList->Close();
|
||||
|
||||
// Clean up threads if any
|
||||
if (soapThread != nullptr)
|
||||
{
|
||||
soapThread->join();
|
||||
delete soapThread;
|
||||
}
|
||||
|
||||
delete raAcceptor;
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
|
||||
StopDB();
|
||||
|
||||
TC_METRIC_EVENT("events", "Worldserver shutdown", "");
|
||||
sMetric->ForceSend();
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Halting process...");
|
||||
|
||||
ShutdownCLIThread(cliThread);
|
||||
|
||||
OpenSSLCrypto::threadsCleanup();
|
||||
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
// 0 - normal shutdown
|
||||
// 1 - shutdown at error
|
||||
// 2 - restart command used, this code can be used by restarter for restart Trinityd
|
||||
@@ -342,9 +373,10 @@ void ShutdownCLIThread(std::thread* cliThread)
|
||||
if (!formatReturnCode)
|
||||
errorBuffer = "Unknown error";
|
||||
|
||||
TC_LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s",
|
||||
uint32(errorCode), errorBuffer);
|
||||
LocalFree(errorBuffer);
|
||||
TC_LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s", uint32(errorCode), errorBuffer);
|
||||
|
||||
if (!formatReturnCode)
|
||||
LocalFree(errorBuffer);
|
||||
|
||||
// send keyboard input to safely unblock the CLI thread
|
||||
INPUT_RECORD b[4];
|
||||
@@ -385,18 +417,6 @@ void ShutdownCLIThread(std::thread* cliThread)
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownThreadPool(std::vector<std::thread>& threadPool)
|
||||
{
|
||||
sScriptMgr->OnNetworkStop();
|
||||
|
||||
_ioService.stop();
|
||||
|
||||
for (auto& thread : threadPool)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldUpdateLoop()
|
||||
{
|
||||
uint32 realCurrTime = 0;
|
||||
@@ -438,33 +458,36 @@ void WorldUpdateLoop()
|
||||
}
|
||||
}
|
||||
|
||||
void SignalHandler(const boost::system::error_code& error, int /*signalNumber*/)
|
||||
void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
|
||||
{
|
||||
if (!error)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
}
|
||||
|
||||
void FreezeDetectorHandler(const boost::system::error_code& error)
|
||||
void FreezeDetector::Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
uint32 curtime = getMSTime();
|
||||
|
||||
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
||||
if (_worldLoopCounter != worldLoopCounter)
|
||||
if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
|
||||
{
|
||||
_lastChangeMsTime = curtime;
|
||||
_worldLoopCounter = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTimeInMs)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
}
|
||||
uint32 curtime = getMSTime();
|
||||
|
||||
_freezeCheckTimer.expires_from_now(boost::posix_time::seconds(1));
|
||||
_freezeCheckTimer.async_wait(FreezeDetectorHandler);
|
||||
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
||||
if (freezeDetector->_worldLoopCounter != worldLoopCounter)
|
||||
{
|
||||
freezeDetector->_lastChangeMsTime = curtime;
|
||||
freezeDetector->_worldLoopCounter = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
}
|
||||
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, freezeDetectorRef, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,13 +497,20 @@ AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService)
|
||||
std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0");
|
||||
|
||||
AsyncAcceptor* acceptor = new AsyncAcceptor(ioService, raListener, raPort);
|
||||
if (!acceptor->Bind())
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Failed to bind RA socket acceptor");
|
||||
delete acceptor;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
acceptor->AsyncAccept<RASession>();
|
||||
return acceptor;
|
||||
}
|
||||
|
||||
bool LoadRealmInfo()
|
||||
bool LoadRealmInfo(boost::asio::io_service& ioService)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver resolver(_ioService);
|
||||
boost::asio::ip::tcp::resolver resolver(ioService);
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
|
||||
QueryResult result = LoginDatabase.PQuery("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE id = %u", realm.Id.Realm);
|
||||
|
||||
Reference in New Issue
Block a user