diff options
Diffstat (limited to 'src/zenhttp/servers/httpasio.cpp')
| -rw-r--r-- | src/zenhttp/servers/httpasio.cpp | 98 |
1 files changed, 63 insertions, 35 deletions
diff --git a/src/zenhttp/servers/httpasio.cpp b/src/zenhttp/servers/httpasio.cpp index e148072c8..3970dea12 100644 --- a/src/zenhttp/servers/httpasio.cpp +++ b/src/zenhttp/servers/httpasio.cpp @@ -161,7 +161,7 @@ public: ~HttpAsioServerImpl(); void Initialize(std::filesystem::path DataDir); - int Start(uint16_t Port, bool ForceLooopback, int ThreadCount); + int Start(uint16_t Port, const AsioConfig& Config); void Stop(); void RegisterService(const char* UrlPath, HttpService& Service); HttpService* RouteRequest(std::string_view Url); @@ -717,7 +717,7 @@ HttpServerConnection::HandleRequest() struct HttpAcceptor { - HttpAcceptor(HttpAsioServerImpl& Server, asio::io_service& IoService, uint16_t BasePort, bool ForceLoopback) + HttpAcceptor(HttpAsioServerImpl& Server, asio::io_service& IoService, uint16_t BasePort, bool ForceLoopback, bool AllowPortProbing) : m_Server(Server) , m_IoService(IoService) , m_Acceptor(m_IoService, asio::ip::tcp::v6()) @@ -751,13 +751,13 @@ struct HttpAcceptor if (IsUsingIPv6) { - BoundBaseUrl = BindAcceptor<asio::ip::address_v6>(BasePort, ForceLoopback); + BoundBaseUrl = BindAcceptor<asio::ip::address_v6>(BasePort, ForceLoopback, AllowPortProbing); } else { ZEN_INFO("NOTE: ipv6 support is disabled, binding to ipv4 only"); - BoundBaseUrl = BindAcceptor<asio::ip::address_v4>(BasePort, ForceLoopback); + BoundBaseUrl = BindAcceptor<asio::ip::address_v4>(BasePort, ForceLoopback, AllowPortProbing); } if (!IsValid()) @@ -816,7 +816,7 @@ struct HttpAcceptor } template<typename AddressType> - std::string BindAcceptor(uint16_t BasePort, bool ForceLoopback) + std::string BindAcceptor(uint16_t BasePort, bool ForceLoopback, bool AllowPortProbing) { uint16_t EffectivePort = BasePort; @@ -858,27 +858,40 @@ struct HttpAcceptor if (BindErrorCode == asio::error::address_in_use) { ZEN_INFO("Desired port {} is in use (error: '{}'), retrying", EffectivePort, BindErrorCode.message()); - Sleep(100); + Sleep(500); m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); } - // Try some alternative ports - for (uint16_t PortOffset = 1; BindErrorCode && (PortOffset < 10); ++PortOffset) + if (AllowPortProbing) { - EffectivePort = BasePort + (PortOffset * 100); - m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); - } + // Try some alternative ports + for (uint16_t PortOffset = 1; BindErrorCode && (PortOffset < 10); ++PortOffset) + { + EffectivePort = BasePort + (PortOffset * 100); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); + } - if (BindErrorCode) - { - ZEN_INFO("Unable to bind to preferred port range, falling back to automatic assignment (error: '{}')", BindErrorCode.message()); + if (BindErrorCode) + { + ZEN_INFO("Unable to bind to preferred port range, falling back to automatic assignment (error: '{}')", + BindErrorCode.message()); - EffectivePort = 0; - m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); + EffectivePort = 0; + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); - if (!BindErrorCode) + if (!BindErrorCode) + { + EffectivePort = m_Acceptor.local_endpoint().port(); + } + } + } + else + { + for (uint32_t Retries = 0; (BindErrorCode == asio::error::address_in_use) && (Retries < 3); Retries++) { - EffectivePort = m_Acceptor.local_endpoint().port(); + ZEN_INFO("Desired port {} is in use (error: '{}'), retrying", EffectivePort, BindErrorCode.message()); + Sleep(500); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); } } @@ -1155,15 +1168,16 @@ HttpAsioServerImpl::Initialize(std::filesystem::path DataDir) } int -HttpAsioServerImpl::Start(uint16_t Port, bool ForceLooopback, int ThreadCount) +HttpAsioServerImpl::Start(uint16_t Port, const AsioConfig& Config) { ZEN_MEMSCOPE(GetHttpasioTag()); - ZEN_ASSERT(ThreadCount > 0); + ZEN_ASSERT(Config.ThreadCount > 0); - ZEN_INFO("starting asio http with {} service threads", ThreadCount); + ZEN_INFO("starting asio http with {} service threads", Config.ThreadCount); - m_Acceptor.reset(new asio_http::HttpAcceptor(*this, m_IoService, Port, ForceLooopback)); + m_Acceptor.reset( + new asio_http::HttpAcceptor(*this, m_IoService, Port, Config.ForceLoopback, /*AllowPortProbing */ !Config.IsDedicatedServer)); if (!m_Acceptor->IsValid()) { @@ -1181,7 +1195,7 @@ HttpAsioServerImpl::Start(uint16_t Port, bool ForceLooopback, int ThreadCount) // from making progress. Or at the very least, thread priorities should // be considered. - for (int i = 0; i < ThreadCount; ++i) + for (unsigned int i = 0; i < Config.ThreadCount; ++i) { m_ThreadPool.emplace_back([this, Index = i + 1] { ZEN_MEMSCOPE(GetHttpasioTag()); @@ -1203,7 +1217,10 @@ HttpAsioServerImpl::Start(uint16_t Port, bool ForceLooopback, int ThreadCount) }); } - ZEN_INFO("asio http started (port {})", m_Acceptor->GetAcceptPort()); + ZEN_INFO("asio http started in {} mode, using {} threads on port {}", + Config.IsDedicatedServer ? "DEDICATED" : "NORMAL", + Config.ThreadCount, + m_Acceptor->GetAcceptPort()); return m_Acceptor->GetAcceptPort(); } @@ -1278,7 +1295,7 @@ namespace zen { class HttpAsioServer : public HttpServer { public: - HttpAsioServer(bool ForceLoopback, unsigned int ThreadCount); + HttpAsioServer(const AsioConfig& Config); ~HttpAsioServer(); virtual void RegisterService(HttpService& Service) override; @@ -1288,17 +1305,15 @@ public: virtual void Close() override; private: - Event m_ShutdownEvent; - int m_BasePort = 0; - bool m_ForceLoopback = false; - unsigned int m_ThreadCount = 0; + Event m_ShutdownEvent; + int m_BasePort = 0; + const AsioConfig m_InitialConfig; std::unique_ptr<asio_http::HttpAsioServerImpl> m_Impl; }; -HttpAsioServer::HttpAsioServer(bool ForceLoopback, unsigned int ThreadCount) -: m_ForceLoopback(ForceLoopback) -, m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(GetHardwareConcurrency(), 8u)) +HttpAsioServer::HttpAsioServer(const AsioConfig& Config) +: m_InitialConfig(Config) , m_Impl(std::make_unique<asio_http::HttpAsioServerImpl>()) { ZEN_DEBUG("Request object size: {} ({:#x})", sizeof(HttpRequestParser), sizeof(HttpRequestParser)); @@ -1338,7 +1353,20 @@ HttpAsioServer::Initialize(int BasePort, std::filesystem::path DataDir) ZEN_TRACE_CPU("HttpAsioServer::Initialize"); m_Impl->Initialize(DataDir); - m_BasePort = m_Impl->Start(gsl::narrow<uint16_t>(BasePort), m_ForceLoopback, m_ThreadCount); + AsioConfig Config = m_InitialConfig; + + Config.ThreadCount = m_InitialConfig.ThreadCount != 0 ? m_InitialConfig.ThreadCount : Max(GetHardwareConcurrency(), 8u); + + if (Config.IsDedicatedServer && m_InitialConfig.ThreadCount == 0) + { + // In order to limit the potential impact of threads stuck + // in locks we allow the thread pool to be oversubscribed + // by a fair amount + + Config.ThreadCount *= 2; + } + + m_BasePort = m_Impl->Start(gsl::narrow<uint16_t>(BasePort), Config); return m_BasePort; } @@ -1394,12 +1422,12 @@ HttpAsioServer::RequestExit() } Ref<HttpServer> -CreateHttpAsioServer(bool ForceLoopback, unsigned int ThreadCount) +CreateHttpAsioServer(const AsioConfig& Config) { ZEN_TRACE_CPU("CreateHttpAsioServer"); ZEN_MEMSCOPE(GetHttpasioTag()); - return Ref<HttpServer>{new HttpAsioServer(ForceLoopback, ThreadCount)}; + return Ref<HttpServer>{new HttpAsioServer(Config)}; } } // namespace zen |