aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-12-05 13:40:08 +0100
committerGitHub Enterprise <[email protected]>2025-12-05 13:40:08 +0100
commit4c0e72df77ea663a92818de225735bc171074486 (patch)
tree0671f41f13de2b4a0bc12ee8fc31bd38cd6d4122 /src
parentcatch exception leaks in windows thread pool (#677) (diff)
downloadzen-4c0e72df77ea663a92818de225735bc171074486.tar.xz
zen-4c0e72df77ea663a92818de225735bc171074486.zip
implement --dedicated option on asio http server (#679)
* implement --dedicated option on asio http server
Diffstat (limited to 'src')
-rw-r--r--src/zenhttp/httpserver.cpp4
-rw-r--r--src/zenhttp/servers/httpasio.cpp98
-rw-r--r--src/zenhttp/servers/httpasio.h9
-rw-r--r--src/zenhttp/servers/httpsys.cpp35
4 files changed, 101 insertions, 45 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp
index b28682375..1357f1b9b 100644
--- a/src/zenhttp/httpserver.cpp
+++ b/src/zenhttp/httpserver.cpp
@@ -1012,7 +1012,9 @@ CreateHttpServerClass(const std::string_view ServerClass, const HttpServerConfig
if (ServerClass == "asio"sv)
{
ZEN_INFO("using asio HTTP server implementation")
- return CreateHttpAsioServer(Config.ForceLoopback, Config.ThreadCount);
+ return CreateHttpAsioServer(AsioConfig{.ThreadCount = Config.ThreadCount,
+ .ForceLoopback = Config.ForceLoopback,
+ .IsDedicatedServer = Config.IsDedicatedServer});
}
#if ZEN_WITH_HTTPSYS
else if (ServerClass == "httpsys"sv)
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
diff --git a/src/zenhttp/servers/httpasio.h b/src/zenhttp/servers/httpasio.h
index 36f988a65..c483dfc28 100644
--- a/src/zenhttp/servers/httpasio.h
+++ b/src/zenhttp/servers/httpasio.h
@@ -6,6 +6,13 @@
namespace zen {
-Ref<HttpServer> CreateHttpAsioServer(bool ForceLoopback, unsigned int ThreadCount);
+struct AsioConfig
+{
+ unsigned int ThreadCount = 0;
+ bool ForceLoopback = false;
+ bool IsDedicatedServer = false;
+};
+
+Ref<HttpServer> CreateHttpAsioServer(const AsioConfig& Config);
} // namespace zen
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index 0be53ffee..9dbdd7167 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -939,7 +939,7 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig)
MaxThreadCount = MinThreadCount * 2;
- if (m_InitialConfig.IsDedicatedServer)
+ if (m_InitialConfig.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
@@ -1042,16 +1042,35 @@ HttpSysServer::InitializeServer(int BasePort)
{
Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0);
- if ((Result == ERROR_SHARING_VIOLATION) && AllowPortProbing)
+ if ((Result == ERROR_SHARING_VIOLATION))
{
- // Sharing violation implies the port is being used by another process
- for (int PortOffset = 1; (Result == ERROR_SHARING_VIOLATION) && (PortOffset < 10); ++PortOffset)
+ ZEN_INFO("Desired port {} is in use (error: '{}'), retrying", EffectivePort, Result);
+ Sleep(500);
+ Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0);
+
+ if (Result == ERROR_SHARING_VIOLATION)
{
- EffectivePort = BasePort + (PortOffset * 100);
- WildcardUrlPath.Reset();
- WildcardUrlPath << u8"http://*:"sv << int64_t(EffectivePort) << u8"/"sv;
+ if (AllowPortProbing)
+ {
+ // Sharing violation implies the port is being used by another process
+ for (int PortOffset = 1; (Result == ERROR_SHARING_VIOLATION) && (PortOffset < 10); ++PortOffset)
+ {
+ EffectivePort = BasePort + (PortOffset * 100);
+ WildcardUrlPath.Reset();
+ WildcardUrlPath << u8"http://*:"sv << int64_t(EffectivePort) << u8"/"sv;
- Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0);
+ Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0);
+ }
+ }
+ else
+ {
+ for (uint32_t Retries = 0; (Result == ERROR_SHARING_VIOLATION) && (Retries < 3); Retries++)
+ {
+ ZEN_INFO("Desired port {} is in use (error: '{}'), retrying", EffectivePort, Result);
+ Sleep(500);
+ Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0);
+ }
+ }
}
}
}