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:
Shauren
2017-05-04 20:41:22 +02:00
parent 3b873add19
commit 6c92a481a3
8 changed files with 293 additions and 198 deletions

View File

@@ -58,23 +58,16 @@ char serviceDescription[] = "TrinityCore Battle.net emulator authentication serv
*/ */
int m_ServiceStatus = -1; int m_ServiceStatus = -1;
static boost::asio::deadline_timer* _serviceStatusWatchTimer; void ServiceStatusWatcher(std::weak_ptr<boost::asio::deadline_timer> serviceStatusWatchTimerRef, std::weak_ptr<boost::asio::io_service> ioServiceRef, boost::system::error_code const& error);
void ServiceStatusWatcher(boost::system::error_code const& error);
#endif #endif
bool StartDB(); bool StartDB();
void StopDB(); void StopDB();
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);
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);
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);
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService); 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) int main(int argc, char** argv)
{ {
signal(SIGABRT, &Trinity::AbortHandler); signal(SIGABRT, &Trinity::AbortHandler);
@@ -88,6 +81,8 @@ int main(int argc, char** argv)
GOOGLE_PROTOBUF_VERIFY_VERSION; GOOGLE_PROTOBUF_VERIFY_VERSION;
std::shared_ptr<void> protobufHandle(nullptr, [](void*) { google::protobuf::ShutdownProtobufLibrary(); });
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
if (configService.compare("install") == 0) if (configService.compare("install") == 0)
return WinServiceInstall() ? 0 : 1; return WinServiceInstall() ? 0 : 1;
@@ -150,86 +145,85 @@ int main(int argc, char** argv)
if (!StartDB()) if (!StartDB())
return 1; 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 // Start the listening port (acceptor) for auth connections
int32 bnport = sConfigMgr->GetIntDefault("BattlenetPort", 1119); int32 bnport = sConfigMgr->GetIntDefault("BattlenetPort", 1119);
if (bnport < 0 || bnport > 0xFFFF) if (bnport < 0 || bnport > 0xFFFF)
{ {
TC_LOG_ERROR("server.bnetserver", "Specified battle.net port (%d) out of allowed range (1-65535)", bnport); TC_LOG_ERROR("server.bnetserver", "Specified battle.net port (%d) out of allowed range (1-65535)", bnport);
StopDB();
delete _ioService;
return 1; return 1;
} }
if (!sLoginService.Start(*_ioService)) if (!sLoginService.Start(*ioService))
{ {
StopDB();
delete _ioService;
TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service"); TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service");
return 1; return 1;
} }
std::shared_ptr<void> sLoginServiceHandle(nullptr, [](void*) { sLoginService.Stop(); });
// Get the list of realms for the server // 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"); 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 // 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 #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
signals.add(SIGBREAK); signals.add(SIGBREAK);
#endif #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 // Set process priority according to configuration settings
SetProcessPriority("server.bnetserver"); SetProcessPriority("server.bnetserver");
// Enabled a timed callback for handling the database keep alive ping // Enabled a timed callback for handling the database keep alive ping
_dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); int32 dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30);
_dbPingTimer = new boost::asio::deadline_timer(*_ioService); 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->expires_from_now(boost::posix_time::minutes(dbPingInterval));
_dbPingTimer->async_wait(KeepDatabaseAliveHandler); dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, std::weak_ptr<boost::asio::deadline_timer>(dbPingTimer), dbPingInterval, std::placeholders::_1));
_banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); int32 banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60);
_banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService); 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->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
_banExpiryCheckTimer->async_wait(BanExpiryHandler); banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, std::weak_ptr<boost::asio::deadline_timer>(banExpiryCheckTimer), banExpiryCheckInterval, std::placeholders::_1));
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
std::shared_ptr<boost::asio::deadline_timer> serviceStatusWatchTimer;
if (m_ServiceStatus != -1) if (m_ServiceStatus != -1)
{ {
_serviceStatusWatchTimer = new boost::asio::deadline_timer(*_ioService); serviceStatusWatchTimer = std::make_shared<boost::asio::deadline_timer>(*ioService);
_serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1));
_serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); 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 #endif
// Start the io service worker loop // Start the io service worker loop
_ioService->run(); ioService->run();
_banExpiryCheckTimer->cancel(); banExpiryCheckTimer->cancel();
_dbPingTimer->cancel(); dbPingTimer->cancel();
sLoginService.Stop();
sSessionMgr.StopNetwork();
sRealmList->Close();
// Close the Database Pool and library
StopDB();
TC_LOG_INFO("server.bnetserver", "Halting process..."); TC_LOG_INFO("server.bnetserver", "Halting process...");
signals.cancel(); signals.cancel();
delete _banExpiryCheckTimer;
delete _dbPingTimer;
delete _ioService;
google::protobuf::ShutdownProtobufLibrary();
return 0; return 0;
} }
@@ -258,51 +252,60 @@ void StopDB()
MySQL::Library_End(); 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) 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) if (!error)
{ {
TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive"); if (std::shared_ptr<boost::asio::deadline_timer> dbPingTimer = dbPingTimerRef.lock())
LoginDatabase.KeepAlive(); {
TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive");
LoginDatabase.KeepAlive();
_dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval));
_dbPingTimer->async_wait(KeepDatabaseAliveHandler); 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) if (!error)
{ {
LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); if (std::shared_ptr<boost::asio::deadline_timer> banExpiryCheckTimer = banExpiryCheckTimerRef.lock())
LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); {
LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_EXPIRED_ACCOUNT_BANNED)); 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->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
_banExpiryCheckTimer->async_wait(BanExpiryHandler); banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, banExpiryCheckTimerRef, banExpiryCheckInterval, std::placeholders::_1));
}
} }
} }
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS #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 (!error)
{ {
if (m_ServiceStatus == 0) if (std::shared_ptr<boost::asio::io_service> ioService = ioServiceRef.lock())
{ {
_ioService->stop(); if (m_ServiceStatus == 0)
delete _serviceStatusWatchTimer; {
} ioService->stop();
else }
{ else if (std::shared_ptr<boost::asio::deadline_timer> serviceStatusWatchTimer = serviceStatusWatchTimerRef.lock())
_serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); {
_serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1));
serviceStatusWatchTimer->async_wait(std::bind(&ServiceStatusWatcher, serviceStatusWatchTimerRef, ioService, std::placeholders::_1));
}
} }
} }
} }

View File

@@ -202,7 +202,7 @@ void WorldSocket::InitializeHandler(boost::system::error_code error, std::size_t
bool WorldSocket::Update() bool WorldSocket::Update()
{ {
EncryptablePacket* queued; EncryptablePacket* queued;
MessageBuffer buffer; MessageBuffer buffer(_sendBufferSize);
while (_bufferQueue.Dequeue(queued)) while (_bufferQueue.Dequeue(queued))
{ {
uint32 packetSize = queued->size(); uint32 packetSize = queued->size();
@@ -212,7 +212,7 @@ bool WorldSocket::Update()
if (buffer.GetRemainingSpace() < packetSize + SizeOfServerHeader) if (buffer.GetRemainingSpace() < packetSize + SizeOfServerHeader)
{ {
QueuePacket(std::move(buffer)); QueuePacket(std::move(buffer));
buffer.Resize(4096); buffer.Resize(_sendBufferSize);
} }
if (buffer.GetRemainingSpace() >= packetSize + SizeOfServerHeader) if (buffer.GetRemainingSpace() >= packetSize + SizeOfServerHeader)

View File

@@ -87,6 +87,7 @@ public:
void SendAuthResponseError(uint32 code); void SendAuthResponseError(uint32 code);
void SetWorldSession(WorldSession* session); void SetWorldSession(WorldSession* session);
void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; }
protected: protected:
void OnClose() override; void OnClose() override;
@@ -142,6 +143,7 @@ private:
MessageBuffer _headerBuffer; MessageBuffer _headerBuffer;
MessageBuffer _packetBuffer; MessageBuffer _packetBuffer;
MPSCQueue<EncryptablePacket> _bufferQueue; MPSCQueue<EncryptablePacket> _bufferQueue;
std::size_t _sendBufferSize;
z_stream_s* _compressionStream; z_stream_s* _compressionStream;

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> * 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 * 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 * under the terms of the GNU General Public License as published by the
@@ -34,6 +34,7 @@ class WorldSocketThread : public NetworkThread<WorldSocket>
public: public:
void SocketAdded(std::shared_ptr<WorldSocket> sock) override void SocketAdded(std::shared_ptr<WorldSocket> sock) override
{ {
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
sScriptMgr->OnSocketOpen(sock); 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); TC_LOG_DEBUG("misc", "Max allowed socket connections %d", max_connections);
// -1 means use default // -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"); TC_LOG_ERROR("misc", "Network.OutUBuff is wrong in your config file");
return false; return false;
} }
BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount); if (!BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount))
_instanceAcceptor = new AsyncAcceptor(service, bindIp, uint16(sWorld->getIntConfig(CONFIG_PORT_INSTANCE))); 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)); _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this));
_instanceAcceptor->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() void WorldSocketMgr::StopNetwork()
{ {
_instanceAcceptor->Close(); if (_instanceAcceptor)
_instanceAcceptor->Close();
BaseSocketMgr::StopNetwork(); BaseSocketMgr::StopNetwork();
delete _instanceAcceptor; delete _instanceAcceptor;
@@ -103,10 +125,10 @@ void WorldSocketMgr::StopNetwork()
void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
{ {
// set some options here // set some options here
if (_socketSendBufferSize >= 0) if (_socketSystemSendBufferSize >= 0)
{ {
boost::system::error_code err; 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) 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()); TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str());

View File

@@ -47,6 +47,8 @@ public:
void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override; void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override;
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
protected: protected:
WorldSocketMgr(); WorldSocketMgr();
@@ -54,8 +56,8 @@ protected:
private: private:
AsyncAcceptor* _instanceAcceptor; AsyncAcceptor* _instanceAcceptor;
int32 _socketSendBufferSize; int32 _socketSystemSendBufferSize;
int32 m_SockOutUBuff; int32 _socketApplicationSendBufferSize;
bool _tcpNoDelay; bool _tcpNoDelay;
}; };

View File

@@ -31,7 +31,7 @@ public:
typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex);
AsyncAcceptor(boost::asio::io_service& ioService, std::string const& bindIp, uint16 port) : 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)) _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() void Close()
{ {
if (_closed.exchange(true)) if (_closed.exchange(true))
@@ -81,6 +108,7 @@ private:
std::pair<tcp::socket*, uint32> DefeaultSocketFactory() { return std::make_pair(&_socket, 0); } std::pair<tcp::socket*, uint32> DefeaultSocketFactory() { return std::make_pair(&_socket, 0); }
tcp::acceptor _acceptor; tcp::acceptor _acceptor;
tcp::endpoint _endpoint;
tcp::socket _socket; tcp::socket _socket;
std::atomic<bool> _closed; std::atomic<bool> _closed;
std::function<std::pair<tcp::socket*, uint32>()> _socketFactory; std::function<std::pair<tcp::socket*, uint32>()> _socketFactory;

View File

@@ -39,9 +39,10 @@ public:
{ {
ASSERT(threadCount > 0); ASSERT(threadCount > 0);
AsyncAcceptor* acceptor = nullptr;
try try
{ {
_acceptor = new AsyncAcceptor(service, bindIp, port); acceptor = new AsyncAcceptor(service, bindIp, port);
} }
catch (boost::system::system_error const& err) catch (boost::system::system_error const& err)
{ {
@@ -49,6 +50,13 @@ public:
return false; return false;
} }
if (!acceptor->Bind())
{
TC_LOG_ERROR("network", "StartNetwork failed to bind socket acceptor");
return false;
}
_acceptor = acceptor;
_threadCount = threadCount; _threadCount = threadCount;
_threads = CreateThreads(); _threads = CreateThreads();
@@ -119,7 +127,7 @@ public:
} }
protected: protected:
SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(1) SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(0)
{ {
} }

View File

@@ -78,22 +78,35 @@ char serviceDescription[] = "TrinityCore World of Warcraft emulator world servic
int m_ServiceStatus = -1; int m_ServiceStatus = -1;
#endif #endif
boost::asio::io_service _ioService; class FreezeDetector
boost::asio::deadline_timer _freezeCheckTimer(_ioService); {
uint32 _worldLoopCounter(0); public:
uint32 _lastChangeMsTime(0); FreezeDetector(boost::asio::io_service& ioService, uint32 maxCoreStuckTime)
uint32 _maxCoreStuckTimeInMs(0); : _timer(ioService), _worldLoopCounter(0), _lastChangeMsTime(0), _maxCoreStuckTimeInMs(maxCoreStuckTime) { }
void SignalHandler(const boost::system::error_code& error, int signalNumber); static void Start(std::shared_ptr<FreezeDetector> const& freezeDetector)
void FreezeDetectorHandler(const boost::system::error_code& error); {
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); AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService);
bool StartDB(); bool StartDB();
void StopDB(); void StopDB();
void WorldUpdateLoop(); void WorldUpdateLoop();
void ClearOnlineAccounts(); void ClearOnlineAccounts();
void ShutdownCLIThread(std::thread* cliThread); void ShutdownCLIThread(std::thread* cliThread);
void ShutdownThreadPool(std::vector<std::thread>& threadPool); bool LoadRealmInfo(boost::asio::io_service& ioService);
bool LoadRealmInfo();
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service); variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service);
/// Launch the Trinity server /// Launch the Trinity server
@@ -111,6 +124,8 @@ extern int main(int argc, char** argv)
GOOGLE_PROTOBUF_VERIFY_VERSION; GOOGLE_PROTOBUF_VERIFY_VERSION;
std::shared_ptr<void> protobufHandle(nullptr, [](void*) { google::protobuf::ShutdownProtobufLibrary(); });
#ifdef _WIN32 #ifdef _WIN32
if (configService.compare("install") == 0) if (configService.compare("install") == 0)
return WinServiceInstall() ? 0 : 1; return WinServiceInstall() ? 0 : 1;
@@ -129,9 +144,11 @@ extern int main(int argc, char** argv)
return 1; return 1;
} }
std::shared_ptr<boost::asio::io_service> ioService = std::make_shared<boost::asio::io_service>();
sLog->RegisterAppender<AppenderDB>(); sLog->RegisterAppender<AppenderDB>();
// If logs are supposed to be handled async then we need to pass the io_service into the Log singleton // 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", Trinity::Banner::Show("worldserver-daemon",
[](char const* text) [](char const* text)
@@ -148,6 +165,8 @@ extern int main(int argc, char** argv)
OpenSSLCrypto::threadsSetup(); OpenSSLCrypto::threadsSetup();
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
// Seed the OpenSSL's PRNG here. // Seed the OpenSSL's PRNG here.
// That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login // That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
BigNumber seed; 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) // 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 #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
signals.add(SIGBREAK); signals.add(SIGBREAK);
#endif #endif
@@ -175,63 +194,87 @@ extern int main(int argc, char** argv)
// Start the Boost based thread pool // Start the Boost based thread pool
int numThreads = sConfigMgr->GetIntDefault("ThreadPool", 1); 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) if (numThreads < 1)
numThreads = 1; numThreads = 1;
for (int i = 0; i < numThreads; ++i) 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 // Set process priority according to configuration settings
SetProcessPriority("server.worldserver"); SetProcessPriority("server.worldserver");
// Start the databases // Start the databases
if (!StartDB()) if (!StartDB())
{
ShutdownThreadPool(threadPool);
return 1; return 1;
}
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
// Set server offline (not connectable) // Set server offline (not connectable)
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm); 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_VALUE("online_players", sWorld->GetPlayerCount());
}); });
TC_METRIC_EVENT("events", "Worldserver started", ""); 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); sScriptMgr->SetScriptLoader(AddScripts);
std::shared_ptr<void> sScriptMgrHandle(nullptr, [](void*)
{
sScriptMgr->Unload();
sScriptReloadMgr->Unload();
});
// Initialize the World
sWorld->SetInitialWorldSettings(); sWorld->SetInitialWorldSettings();
// Launch CliRunnable thread std::shared_ptr<void> mapManagementHandle(nullptr, [](void*)
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
{ {
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 // Start the Remote Access port (acceptor) if enabled
AsyncAcceptor* raAcceptor = nullptr; std::unique_ptr<AsyncAcceptor> raAcceptor;
if (sConfigMgr->GetBoolDefault("Ra.Enable", false)) if (sConfigMgr->GetBoolDefault("Ra.Enable", false))
raAcceptor = StartRaSocketAcceptor(_ioService); raAcceptor.reset(StartRaSocketAcceptor(*ioService));
// Start soap serving thread if enabled // Start soap serving thread if enabled
std::thread* soapThread = nullptr; std::shared_ptr<std::thread> soapThread;
if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false)) 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 // Launch the worldserver listener socket
@@ -243,10 +286,36 @@ extern int main(int argc, char** argv)
if (networkThreads <= 0) if (networkThreads <= 0)
{ {
TC_LOG_ERROR("server.worldserver", "Network.Threads must be greater than 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) // 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); 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)); realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_OFFLINE));
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec) // 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)) if (int coreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0))
{ {
_maxCoreStuckTimeInMs = coreStuckTime * 1000; freezeDetector = std::make_shared<FreezeDetector>(*ioService, coreStuckTime * 1000);
_freezeCheckTimer.expires_from_now(boost::posix_time::seconds(5)); freezeDetector->Start(freezeDetector);
_freezeCheckTimer.async_wait(FreezeDetectorHandler);
TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", coreStuckTime); 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(); WorldUpdateLoop();
// Shutdown starts here // Shutdown starts here
ShutdownThreadPool(threadPool); threadPool.reset();
sLog->SetSynchronous(); sLog->SetSynchronous();
sScriptMgr->OnShutdown(); 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 // set server offline
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm); 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..."); TC_LOG_INFO("server.worldserver", "Halting process...");
ShutdownCLIThread(cliThread);
OpenSSLCrypto::threadsCleanup();
google::protobuf::ShutdownProtobufLibrary();
// 0 - normal shutdown // 0 - normal shutdown
// 1 - shutdown at error // 1 - shutdown at error
// 2 - restart command used, this code can be used by restarter for restart Trinityd // 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) if (!formatReturnCode)
errorBuffer = "Unknown error"; errorBuffer = "Unknown error";
TC_LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s", TC_LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s", uint32(errorCode), errorBuffer);
uint32(errorCode), errorBuffer);
LocalFree(errorBuffer); if (!formatReturnCode)
LocalFree(errorBuffer);
// send keyboard input to safely unblock the CLI thread // send keyboard input to safely unblock the CLI thread
INPUT_RECORD b[4]; 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() void WorldUpdateLoop()
{ {
uint32 realCurrTime = 0; 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) if (!error)
World::StopNow(SHUTDOWN_EXIT_CODE); 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) if (!error)
{ {
uint32 curtime = getMSTime(); if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
uint32 worldLoopCounter = World::m_worldLoopCounter;
if (_worldLoopCounter != worldLoopCounter)
{ {
_lastChangeMsTime = curtime; uint32 curtime = getMSTime();
_worldLoopCounter = worldLoopCounter;
}
// possible freeze
else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTimeInMs)
{
TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
ABORT();
}
_freezeCheckTimer.expires_from_now(boost::posix_time::seconds(1)); uint32 worldLoopCounter = World::m_worldLoopCounter;
_freezeCheckTimer.async_wait(FreezeDetectorHandler); 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"); std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0");
AsyncAcceptor* acceptor = new AsyncAcceptor(ioService, raListener, raPort); 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>(); acceptor->AsyncAccept<RASession>();
return acceptor; 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; 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); 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);