diff options
author | Shauren <shauren.trinity@gmail.com> | 2017-05-04 20:41:22 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2017-05-04 20:41:22 +0200 |
commit | 6c92a481a34eb19c7ba751a1f7547a39c3d3a088 (patch) | |
tree | 60ba27db4ce444a03ca07a5f76792b37834d3adc /src/server/worldserver/Main.cpp | |
parent | 3b873add1933ffa6593b2e3d7be2033257cffc62 (diff) |
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
Diffstat (limited to 'src/server/worldserver/Main.cpp')
-rw-r--r-- | src/server/worldserver/Main.cpp | 254 |
1 files changed, 142 insertions, 112 deletions
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 1ac22b5c81f..2136b5f7654 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -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); - -void SignalHandler(const boost::system::error_code& error, int signalNumber); -void FreezeDetectorHandler(const boost::system::error_code& error); +class FreezeDetector +{ +public: + FreezeDetector(boost::asio::io_service& ioService, uint32 maxCoreStuckTime) + : _timer(ioService), _worldLoopCounter(0), _lastChangeMsTime(0), _maxCoreStuckTimeInMs(maxCoreStuckTime) { } + + 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)); + + std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); }); - LoadRealmInfo(); + LoadRealmInfo(*ioService); - sMetric->Initialize(realm.Name, _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; + } + + if (!sWorldSocketMgr.StartNetwork(*ioService, worldListener, worldPort, networkThreads)) + { + TC_LOG_ERROR("server.worldserver", "Failed to initialize network"); + return 1; } - sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort, networkThreads); + 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) - { - _lastChangeMsTime = curtime; - _worldLoopCounter = worldLoopCounter; - } - // possible freeze - else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTimeInMs) + if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock()) { - TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); - ABORT(); + uint32 curtime = getMSTime(); + + 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)); } - - _freezeCheckTimer.expires_from_now(boost::posix_time::seconds(1)); - _freezeCheckTimer.async_wait(FreezeDetectorHandler); } } @@ -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); |