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 | |
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
-rw-r--r-- | src/server/bnetserver/Main.cpp | 143 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.h | 2 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketMgr.cpp | 42 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketMgr.h | 6 | ||||
-rw-r--r-- | src/server/shared/Networking/AsyncAcceptor.h | 30 | ||||
-rw-r--r-- | src/server/shared/Networking/SocketMgr.h | 12 | ||||
-rw-r--r-- | src/server/worldserver/Main.cpp | 254 |
8 files changed, 294 insertions, 199 deletions
diff --git a/src/server/bnetserver/Main.cpp b/src/server/bnetserver/Main.cpp index ec6f11c92da..ccee80e058a 100644 --- a/src/server/bnetserver/Main.cpp +++ b/src/server/bnetserver/Main.cpp @@ -58,23 +58,16 @@ char serviceDescription[] = "TrinityCore Battle.net emulator authentication serv */ int m_ServiceStatus = -1; -static boost::asio::deadline_timer* _serviceStatusWatchTimer; -void ServiceStatusWatcher(boost::system::error_code const& error); +void ServiceStatusWatcher(std::weak_ptr<boost::asio::deadline_timer> serviceStatusWatchTimerRef, std::weak_ptr<boost::asio::io_service> ioServiceRef, boost::system::error_code const& error); #endif bool StartDB(); void StopDB(); -void SignalHandler(boost::system::error_code const& error, int signalNumber); -void KeepDatabaseAliveHandler(boost::system::error_code const& error); -void BanExpiryHandler(boost::system::error_code const& error); +void SignalHandler(std::weak_ptr<boost::asio::io_service> ioServiceRef, boost::system::error_code const& error, int signalNumber); +void KeepDatabaseAliveHandler(std::weak_ptr<boost::asio::deadline_timer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error); +void BanExpiryHandler(std::weak_ptr<boost::asio::deadline_timer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error); variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService); -static boost::asio::io_service* _ioService; -static boost::asio::deadline_timer* _dbPingTimer; -static uint32 _dbPingInterval; -static boost::asio::deadline_timer* _banExpiryCheckTimer; -static uint32 _banExpiryCheckInterval; - int main(int argc, char** argv) { signal(SIGABRT, &Trinity::AbortHandler); @@ -88,6 +81,8 @@ int main(int argc, char** argv) GOOGLE_PROTOBUF_VERIFY_VERSION; + std::shared_ptr<void> protobufHandle(nullptr, [](void*) { google::protobuf::ShutdownProtobufLibrary(); }); + #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS if (configService.compare("install") == 0) return WinServiceInstall() ? 0 : 1; @@ -150,86 +145,85 @@ int main(int argc, char** argv) if (!StartDB()) return 1; - _ioService = new boost::asio::io_service(); + std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); }); + + std::shared_ptr<boost::asio::io_service> ioService = std::make_shared<boost::asio::io_service>(); // Start the listening port (acceptor) for auth connections int32 bnport = sConfigMgr->GetIntDefault("BattlenetPort", 1119); if (bnport < 0 || bnport > 0xFFFF) { TC_LOG_ERROR("server.bnetserver", "Specified battle.net port (%d) out of allowed range (1-65535)", bnport); - StopDB(); - delete _ioService; return 1; } - if (!sLoginService.Start(*_ioService)) + if (!sLoginService.Start(*ioService)) { - StopDB(); - delete _ioService; TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service"); return 1; } + std::shared_ptr<void> sLoginServiceHandle(nullptr, [](void*) { sLoginService.Stop(); }); + // Get the list of realms for the server - sRealmList->Initialize(*_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10)); + sRealmList->Initialize(*ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10)); + + std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); }); std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - sSessionMgr.StartNetwork(*_ioService, bindIp, bnport); + if (!sSessionMgr.StartNetwork(*ioService, bindIp, bnport)) + { + TC_LOG_ERROR("server.bnetserver", "Failed to initialize network"); + return 1; + } + + std::shared_ptr<void> sSessionMgrHandle(nullptr, [](void*) { sSessionMgr.StopNetwork(); }); // Set signal handlers - 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 - signals.async_wait(SignalHandler); + signals.async_wait(std::bind(&SignalHandler, std::weak_ptr<boost::asio::io_service>(ioService), std::placeholders::_1, std::placeholders::_2)); // Set process priority according to configuration settings SetProcessPriority("server.bnetserver"); // Enabled a timed callback for handling the database keep alive ping - _dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); - _dbPingTimer = new boost::asio::deadline_timer(*_ioService); - _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); - _dbPingTimer->async_wait(KeepDatabaseAliveHandler); + int32 dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); + std::shared_ptr<boost::asio::deadline_timer> dbPingTimer = std::make_shared<boost::asio::deadline_timer>(*ioService); + dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval)); + dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, std::weak_ptr<boost::asio::deadline_timer>(dbPingTimer), dbPingInterval, std::placeholders::_1)); - _banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); - _banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService); - _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); - _banExpiryCheckTimer->async_wait(BanExpiryHandler); + int32 banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); + std::shared_ptr<boost::asio::deadline_timer> banExpiryCheckTimer = std::make_shared<boost::asio::deadline_timer>(*ioService); + banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval)); + banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, std::weak_ptr<boost::asio::deadline_timer>(banExpiryCheckTimer), banExpiryCheckInterval, std::placeholders::_1)); #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS + std::shared_ptr<boost::asio::deadline_timer> serviceStatusWatchTimer; if (m_ServiceStatus != -1) { - _serviceStatusWatchTimer = new boost::asio::deadline_timer(*_ioService); - _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); - _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); + serviceStatusWatchTimer = std::make_shared<boost::asio::deadline_timer>(*ioService); + serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); + serviceStatusWatchTimer->async_wait(std::bind(&ServiceStatusWatcher, + std::weak_ptr<boost::asio::deadline_timer>(serviceStatusWatchTimer), + std::weak_ptr<boost::asio::io_service>(ioService), + std::placeholders::_1)); } #endif // Start the io service worker loop - _ioService->run(); - - _banExpiryCheckTimer->cancel(); - _dbPingTimer->cancel(); + ioService->run(); - sLoginService.Stop(); - - sSessionMgr.StopNetwork(); - - sRealmList->Close(); - - // Close the Database Pool and library - StopDB(); + banExpiryCheckTimer->cancel(); + dbPingTimer->cancel(); TC_LOG_INFO("server.bnetserver", "Halting process..."); signals.cancel(); - delete _banExpiryCheckTimer; - delete _dbPingTimer; - delete _ioService; - google::protobuf::ShutdownProtobufLibrary(); return 0; } @@ -258,51 +252,60 @@ void StopDB() MySQL::Library_End(); } -void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/) +void SignalHandler(std::weak_ptr<boost::asio::io_service> ioServiceRef, boost::system::error_code const& error, int /*signalNumber*/) { if (!error) - _ioService->stop(); + if (std::shared_ptr<boost::asio::io_service> ioService = ioServiceRef.lock()) + ioService->stop(); } -void KeepDatabaseAliveHandler(boost::system::error_code const& error) +void KeepDatabaseAliveHandler(std::weak_ptr<boost::asio::deadline_timer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error) { if (!error) { - TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive"); - LoginDatabase.KeepAlive(); + if (std::shared_ptr<boost::asio::deadline_timer> dbPingTimer = dbPingTimerRef.lock()) + { + TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive"); + LoginDatabase.KeepAlive(); - _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); - _dbPingTimer->async_wait(KeepDatabaseAliveHandler); + dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval)); + dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, dbPingTimerRef, dbPingInterval, std::placeholders::_1)); + } } } -void BanExpiryHandler(boost::system::error_code const& error) +void BanExpiryHandler(std::weak_ptr<boost::asio::deadline_timer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error) { if (!error) { - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_EXPIRED_ACCOUNT_BANNED)); + if (std::shared_ptr<boost::asio::deadline_timer> banExpiryCheckTimer = banExpiryCheckTimerRef.lock()) + { + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_EXPIRED_ACCOUNT_BANNED)); - _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); - _banExpiryCheckTimer->async_wait(BanExpiryHandler); + banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval)); + banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, banExpiryCheckTimerRef, banExpiryCheckInterval, std::placeholders::_1)); + } } } #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS -void ServiceStatusWatcher(boost::system::error_code const& error) +void ServiceStatusWatcher(std::weak_ptr<boost::asio::deadline_timer> serviceStatusWatchTimerRef, std::weak_ptr<boost::asio::io_service> ioServiceRef, boost::system::error_code const& error) { if (!error) { - if (m_ServiceStatus == 0) - { - _ioService->stop(); - delete _serviceStatusWatchTimer; - } - else + if (std::shared_ptr<boost::asio::io_service> ioService = ioServiceRef.lock()) { - _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); - _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); + if (m_ServiceStatus == 0) + { + ioService->stop(); + } + else if (std::shared_ptr<boost::asio::deadline_timer> serviceStatusWatchTimer = serviceStatusWatchTimerRef.lock()) + { + serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); + serviceStatusWatchTimer->async_wait(std::bind(&ServiceStatusWatcher, serviceStatusWatchTimerRef, ioService, std::placeholders::_1)); + } } } } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index bd5b84f24e3..8069220d270 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -202,7 +202,7 @@ void WorldSocket::InitializeHandler(boost::system::error_code error, std::size_t bool WorldSocket::Update() { EncryptablePacket* queued; - MessageBuffer buffer; + MessageBuffer buffer(_sendBufferSize); while (_bufferQueue.Dequeue(queued)) { uint32 packetSize = queued->size(); @@ -212,7 +212,7 @@ bool WorldSocket::Update() if (buffer.GetRemainingSpace() < packetSize + SizeOfServerHeader) { QueuePacket(std::move(buffer)); - buffer.Resize(4096); + buffer.Resize(_sendBufferSize); } if (buffer.GetRemainingSpace() >= packetSize + SizeOfServerHeader) diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 14ce250d622..337ef055e2a 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -87,6 +87,7 @@ public: void SendAuthResponseError(uint32 code); void SetWorldSession(WorldSession* session); + void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; } protected: void OnClose() override; @@ -142,6 +143,7 @@ private: MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; MPSCQueue<EncryptablePacket> _bufferQueue; + std::size_t _sendBufferSize; z_stream_s* _compressionStream; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 389df8619a1..42f34245adc 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -34,6 +34,7 @@ class WorldSocketThread : public NetworkThread<WorldSocket> public: void SocketAdded(std::shared_ptr<WorldSocket> sock) override { + sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize()); sScriptMgr->OnSocketOpen(sock); } @@ -43,7 +44,7 @@ public: } }; -WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _instanceAcceptor(nullptr), _socketSendBufferSize(-1), m_SockOutUBuff(65536), _tcpNoDelay(true) +WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _instanceAcceptor(nullptr), _socketSystemSendBufferSize(-1), _socketApplicationSendBufferSize(65536), _tcpNoDelay(true) { } @@ -66,18 +67,37 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string TC_LOG_DEBUG("misc", "Max allowed socket connections %d", max_connections); // -1 means use default - _socketSendBufferSize = sConfigMgr->GetIntDefault("Network.OutKBuff", -1); + _socketSystemSendBufferSize = sConfigMgr->GetIntDefault("Network.OutKBuff", -1); - m_SockOutUBuff = sConfigMgr->GetIntDefault("Network.OutUBuff", 65536); + _socketApplicationSendBufferSize = sConfigMgr->GetIntDefault("Network.OutUBuff", 65536); - if (m_SockOutUBuff <= 0) + if (_socketApplicationSendBufferSize <= 0) { TC_LOG_ERROR("misc", "Network.OutUBuff is wrong in your config file"); return false; } - BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount); - _instanceAcceptor = new AsyncAcceptor(service, bindIp, uint16(sWorld->getIntConfig(CONFIG_PORT_INSTANCE))); + if (!BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount)) + return false; + + AsyncAcceptor* instanceAcceptor = nullptr; + try + { + instanceAcceptor = new AsyncAcceptor(service, bindIp, uint16(sWorld->getIntConfig(CONFIG_PORT_INSTANCE))); + } + catch (boost::system::system_error const& err) + { + TC_LOG_ERROR("network", "Exception caught in WorldSocketMgr::StartNetwork (%s:%u): %s", bindIp.c_str(), port, err.what()); + return false; + } + + if (!instanceAcceptor->Bind()) + { + TC_LOG_ERROR("network", "StartNetwork failed to bind instance socket acceptor"); + return false; + } + + _instanceAcceptor = instanceAcceptor; _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); _instanceAcceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); @@ -91,7 +111,9 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string void WorldSocketMgr::StopNetwork() { - _instanceAcceptor->Close(); + if (_instanceAcceptor) + _instanceAcceptor->Close(); + BaseSocketMgr::StopNetwork(); delete _instanceAcceptor; @@ -103,10 +125,10 @@ void WorldSocketMgr::StopNetwork() void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) { // set some options here - if (_socketSendBufferSize >= 0) + if (_socketSystemSendBufferSize >= 0) { boost::system::error_code err; - sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSendBufferSize), err); + sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSystemSendBufferSize), err); if (err && err != boost::system::errc::not_supported) { TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str()); diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h index 6c6933f156b..3540d8778c1 100644 --- a/src/server/game/Server/WorldSocketMgr.h +++ b/src/server/game/Server/WorldSocketMgr.h @@ -47,6 +47,8 @@ public: void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override; + std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; } + protected: WorldSocketMgr(); @@ -54,8 +56,8 @@ protected: private: AsyncAcceptor* _instanceAcceptor; - int32 _socketSendBufferSize; - int32 m_SockOutUBuff; + int32 _socketSystemSendBufferSize; + int32 _socketApplicationSendBufferSize; bool _tcpNoDelay; }; diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index 8a73e6e59fc..8f598c177d2 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -31,7 +31,7 @@ public: typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); AsyncAcceptor(boost::asio::io_service& ioService, std::string const& bindIp, uint16 port) : - _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)), + _acceptor(ioService), _endpoint(boost::asio::ip::address::from_string(bindIp), port), _socket(ioService), _closed(false), _socketFactory(std::bind(&AsyncAcceptor::DefeaultSocketFactory, this)) { } @@ -66,6 +66,33 @@ public: }); } + bool Bind() + { + boost::system::error_code errorCode; + _acceptor.open(_endpoint.protocol(), errorCode); + if (errorCode) + { + TC_LOG_INFO("network", "Failed to open acceptor %s", errorCode.message().c_str()); + return false; + } + + _acceptor.bind(_endpoint, errorCode); + if (errorCode) + { + TC_LOG_INFO("network", "Couldn't bind to %s:%u %s", _endpoint.address().to_string(), _endpoint.port(), errorCode.message().c_str()); + return false; + } + + _acceptor.listen(boost::asio::socket_base::max_connections, errorCode); + if (errorCode) + { + TC_LOG_INFO("network", "Failed to start listening on %s:%u %s", _endpoint.address().to_string(), _endpoint.port(), errorCode.message().c_str()); + return false; + } + + return true; + } + void Close() { if (_closed.exchange(true)) @@ -81,6 +108,7 @@ private: std::pair<tcp::socket*, uint32> DefeaultSocketFactory() { return std::make_pair(&_socket, 0); } tcp::acceptor _acceptor; + tcp::endpoint _endpoint; tcp::socket _socket; std::atomic<bool> _closed; std::function<std::pair<tcp::socket*, uint32>()> _socketFactory; diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h index cb836c86db8..523f6794e68 100644 --- a/src/server/shared/Networking/SocketMgr.h +++ b/src/server/shared/Networking/SocketMgr.h @@ -39,9 +39,10 @@ public: { ASSERT(threadCount > 0); + AsyncAcceptor* acceptor = nullptr; try { - _acceptor = new AsyncAcceptor(service, bindIp, port); + acceptor = new AsyncAcceptor(service, bindIp, port); } catch (boost::system::system_error const& err) { @@ -49,6 +50,13 @@ public: return false; } + if (!acceptor->Bind()) + { + TC_LOG_ERROR("network", "StartNetwork failed to bind socket acceptor"); + return false; + } + + _acceptor = acceptor; _threadCount = threadCount; _threads = CreateThreads(); @@ -119,7 +127,7 @@ public: } protected: - SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(1) + SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(0) { } 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); |