diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/authserver/Main.cpp | 130 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.h | 3 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocketMgr.cpp | 18 | ||||
-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 | 243 |
8 files changed, 260 insertions, 188 deletions
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 1669e0958b7..6e67a950343 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -62,23 +62,16 @@ char serviceDescription[] = "TrinityCore World of Warcraft emulator auth service */ int m_ServiceStatus = -1; -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(const boost::system::error_code& error, int signalNumber); -void KeepDatabaseAliveHandler(const boost::system::error_code& 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); -boost::asio::io_service* _ioService; -boost::asio::deadline_timer* _dbPingTimer; -uint32 _dbPingInterval; -boost::asio::deadline_timer* _banExpiryCheckTimer; -uint32 _banExpiryCheckInterval; - int main(int argc, char** argv) { signal(SIGABRT, &Trinity::AbortHandler); @@ -134,16 +127,18 @@ 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>(); // Get the list of realms for the server - sRealmList->Initialize(*_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); + sRealmList->Initialize(*ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); + + std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); }); if (sRealmList->GetRealms().empty()) { TC_LOG_ERROR("server.authserver", "No valid realms specified."); - StopDB(); - delete _ioService; return 1; } @@ -152,65 +147,63 @@ int main(int argc, char** argv) if (port < 0 || port > 0xFFFF) { TC_LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)"); - StopDB(); - delete _ioService; return 1; } std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - sAuthSocketMgr.StartNetwork(*_ioService, bindIp, port); + if (!sAuthSocketMgr.StartNetwork(*ioService, bindIp, port)) + { + TC_LOG_ERROR("server.authserver", "Failed to initialize network"); + return 1; + } + + std::shared_ptr<void> sAuthSocketMgrHandle(nullptr, [](void*) { sAuthSocketMgr.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.authserver"); // 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(); - - sAuthSocketMgr.StopNetwork(); + ioService->run(); - sRealmList->Close(); - - // Close the Database Pool and library - StopDB(); + banExpiryCheckTimer->cancel(); + dbPingTimer->cancel(); TC_LOG_INFO("server.authserver", "Halting process..."); signals.cancel(); - delete _banExpiryCheckTimer; - delete _dbPingTimer; - delete _ioService; return 0; } @@ -241,50 +234,57 @@ void StopDB() MySQL::Library_End(); } -void SignalHandler(const boost::system::error_code& 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(const boost::system::error_code& 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.authserver", "Ping MySQL to keep connection alive"); - LoginDatabase.KeepAlive(); + if (std::shared_ptr<boost::asio::deadline_timer> dbPingTimer = dbPingTimerRef.lock()) + { + TC_LOG_INFO("server.authserver", "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)); + 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)); - _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, ioServiceRef, std::placeholders::_1)); + } } } } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index b22d87038ba..ae8b3fd0998 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -40,7 +40,7 @@ private: using boost::asio::ip::tcp; WorldSocket::WorldSocket(tcp::socket&& socket) - : Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _authed(false) + : Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _authed(false), _sendBufferSize(4096) { _headerBuffer.Resize(sizeof(ClientPktHeader)); } @@ -87,7 +87,7 @@ void WorldSocket::CheckIpCallback(PreparedQueryResult result) bool WorldSocket::Update() { EncryptablePacket* queued; - MessageBuffer buffer; + MessageBuffer buffer(_sendBufferSize); while (_bufferQueue.Dequeue(queued)) { ServerPktHeader header(queued->size() + 2, queued->GetOpcode()); @@ -97,7 +97,7 @@ bool WorldSocket::Update() if (buffer.GetRemainingSpace() < queued->size() + header.getHeaderLength()) { QueuePacket(std::move(buffer)); - buffer.Resize(4096); + buffer.Resize(_sendBufferSize); } if (buffer.GetRemainingSpace() >= queued->size() + header.getHeaderLength()) diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 36ce6528beb..e8ea99cdb88 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -67,6 +67,8 @@ public: void SendPacket(WorldPacket const& packet); + void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; } + protected: void OnClose() override; void ReadHandler() override; @@ -110,6 +112,7 @@ private: MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; MPSCQueue<EncryptablePacket> _bufferQueue; + std::size_t _sendBufferSize; QueryCallbackProcessor _queryProcessor; std::string _ipCountry; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 6e6fd5cf99f..5c392f23669 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(), _socketSendBufferSize(-1), m_SockOutUBuff(65536), _tcpNoDelay(true) +WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _socketSystemSendBufferSize(-1), _socketApplicationSendBufferSize(65536), _tcpNoDelay(true) { } @@ -61,17 +62,18 @@ 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); + if (!BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount)) + return false; _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); @@ -91,10 +93,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 a5aee344bf7..ce81acfea78 100644 --- a/src/server/game/Server/WorldSocketMgr.h +++ b/src/server/game/Server/WorldSocketMgr.h @@ -45,14 +45,16 @@ public: void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override; + std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; } + protected: WorldSocketMgr(); NetworkThread<WorldSocket>* CreateThreads() const override; private: - 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..7c827ec4454 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", "Could not bind to %s:%u %s", _endpoint.address().to_string().c_str(), _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().c_str(), _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 df8bc252939..21c30ec0fa1 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -77,22 +77,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 @@ -126,9 +139,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); TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", GitRevision::GetFullVersion()); TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n"); @@ -147,6 +162,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; @@ -166,7 +183,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 @@ -174,61 +191,83 @@ 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); - 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,7 +282,33 @@ extern int main(int argc, char** argv) 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); @@ -251,11 +316,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); } @@ -266,52 +331,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(); - 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); - // 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(); - // 0 - normal shutdown // 1 - shutdown at error // 2 - restart command used, this code can be used by restarter for restart Trinityd @@ -336,9 +366,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]; @@ -379,18 +410,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; @@ -423,33 +442,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); } } @@ -459,13 +481,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 FROM realmlist WHERE id = %u", realm.Id.Realm); |