diff options
| author | Stefan Boberg <[email protected]> | 2023-10-12 15:27:55 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2023-10-12 15:27:55 +0200 |
| commit | 83f4c7f9f564febbcc5895337e2cbc340d7da441 (patch) | |
| tree | ff3514b444bc6943e2668f8f145bf1c9b00556fc /src/zenhttp/httpsys.cpp | |
| parent | Change default port to 8558 (diff) | |
| parent | Update README.md (diff) | |
| download | zen-83f4c7f9f564febbcc5895337e2cbc340d7da441.tar.xz zen-83f4c7f9f564febbcc5895337e2cbc340d7da441.zip | |
Merge remote-tracking branch 'origin/main' into zs/default-port-change
Diffstat (limited to 'src/zenhttp/httpsys.cpp')
| -rw-r--r-- | src/zenhttp/httpsys.cpp | 82 |
1 files changed, 68 insertions, 14 deletions
diff --git a/src/zenhttp/httpsys.cpp b/src/zenhttp/httpsys.cpp index 993b065c0..c1b4717cb 100644 --- a/src/zenhttp/httpsys.cpp +++ b/src/zenhttp/httpsys.cpp @@ -32,7 +32,10 @@ namespace zen { /** * @brief Windows implementation of HTTP server based on http.sys * - * This requires elevation to function + * This requires elevation to function by default but system configuration + * can soften this requirement. + * + * See README.md for details. */ class HttpSysServer : public HttpServer { @@ -76,7 +79,8 @@ private: bool m_IsRequestLoggingEnabled = false; bool m_IsAsyncResponseEnabled = true; - WinIoThreadPool m_ThreadPool; + std::unique_ptr<WinIoThreadPool> m_IoThreadPool; + RwLock m_AsyncWorkPoolInitLock; WorkerThreadPool* m_AsyncWorkPool = nullptr; @@ -89,7 +93,7 @@ private: int32_t m_MinPendingRequests = 16; int32_t m_MaxPendingRequests = 128; Event m_ShutdownEvent; - const HttpSysConfig m_InitialConfig; + HttpSysConfig m_InitialConfig; }; } // namespace zen @@ -889,14 +893,45 @@ HttpAsyncWorkRequest::AsyncWorkItem::Execute() \/ \/ \/ */ -HttpSysServer::HttpSysServer(const HttpSysConfig& Config) +HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig) : m_Log(logging::Get("http")) , m_RequestLog(logging::Get("http_requests")) -, m_IsRequestLoggingEnabled(Config.IsRequestLoggingEnabled) -, m_IsAsyncResponseEnabled(Config.IsAsyncResponseEnabled) -, m_ThreadPool(Config.ThreadCount != 0 ? Config.ThreadCount : std::thread::hardware_concurrency()) -, m_InitialConfig(Config) +, m_IsRequestLoggingEnabled(InConfig.IsRequestLoggingEnabled) +, m_IsAsyncResponseEnabled(InConfig.IsAsyncResponseEnabled) +, m_InitialConfig(InConfig) { + // Initialize thread pool + + int MinThreadCount; + int MaxThreadCount; + + if (m_InitialConfig.ThreadCount == 0) + { + MinThreadCount = Max(8u, std::thread::hardware_concurrency()); + } + else + { + MinThreadCount = m_InitialConfig.ThreadCount; + } + + MaxThreadCount = MinThreadCount * 2; + + if (m_InitialConfig.IsDedicatedServer) + { + // In order to limit the potential impact of threads stuck + // in locks we allow the thread pool to be oversubscribed + // by a fair amount + + MaxThreadCount *= 2; + } + + m_IoThreadPool = std::make_unique<WinIoThreadPool>(MinThreadCount, MaxThreadCount); + + if (m_InitialConfig.AsyncWorkThreadCount == 0) + { + m_InitialConfig.AsyncWorkThreadCount = 16; + } + ULONG Result = HttpInitialize(HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, nullptr); if (Result != NO_ERROR) @@ -907,7 +942,11 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& Config) m_IsHttpInitialized = true; m_IsOk = true; - ZEN_INFO("http.sys server started, using {} I/O threads and {} async worker threads", Config.ThreadCount, Config.AsyncWorkThreadCount); + ZEN_INFO("http.sys server started in {} mode, using {}-{} I/O threads and {} async worker threads", + m_InitialConfig.IsDedicatedServer ? "DEDICATED" : "NORMAL", + MinThreadCount, + MaxThreadCount, + m_InitialConfig.AsyncWorkThreadCount); } HttpSysServer::~HttpSysServer() @@ -1070,10 +1109,27 @@ HttpSysServer::InitializeServer(int BasePort) 0); } + // Tune the maximum number of pending requests in the http.sys request queue. By default + // the value is 1000 which is plenty for single user machines but for dedicated servers + // serving many users it makes sense to increase this to a higher number to help smooth + // out intermittent stalls like we might experience when GC is triggered + + if (m_InitialConfig.IsDedicatedServer) + { + ULONG QueueLength = 50000; + + Result = HttpSetRequestQueueProperty(m_RequestQueueHandle, HttpServerQueueLengthProperty, &QueueLength, sizeof QueueLength, 0, 0); + + if (Result != NO_ERROR) + { + ZEN_WARN("changing request queue length to {} failed: {}", QueueLength, Result); + } + } + // Create I/O completion port std::error_code ErrorCode; - m_ThreadPool.CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode); + m_IoThreadPool->CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode); if (ErrorCode) { @@ -1137,9 +1193,7 @@ HttpSysServer::WorkPool() if (!m_AsyncWorkPool) { - unsigned int WorkerThreadCount = m_InitialConfig.AsyncWorkThreadCount != 0 ? m_InitialConfig.AsyncWorkThreadCount : 16; - - m_AsyncWorkPool = new WorkerThreadPool(WorkerThreadCount, "http_async"); + m_AsyncWorkPool = new WorkerThreadPool(m_InitialConfig.AsyncWorkThreadCount, "http_async"); } } @@ -1305,7 +1359,7 @@ HttpSysTransaction::~HttpSysTransaction() PTP_IO HttpSysTransaction::Iocp() { - return m_HttpServer.m_ThreadPool.Iocp(); + return m_HttpServer.m_IoThreadPool->Iocp(); } HANDLE |