aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/servers/httpsys.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-02-20 14:43:32 +0100
committerStefan Boberg <[email protected]>2026-02-20 14:43:32 +0100
commitc7d033755c1566f7908e3a66bced6b3c3d062d70 (patch)
treee0c46b7fcd5599720a9194830b2c229bc6738dd3 /src/zenhttp/servers/httpsys.cpp
parentupdate .gitignore to exclude .claude/ (diff)
downloadzen-c7d033755c1566f7908e3a66bced6b3c3d062d70.tar.xz
zen-c7d033755c1566f7908e3a66bced6b3c3d062d70.zip
added new I/O thread pool implementation
the new variant manages a dynamically growing/shrinking set of threads manually instead of relying on the built-in Windows thread pool the benefit of this is that we're in charge of setup and teardown so can make better guarantees about lifetimes of threads which can help with shutdown issues
Diffstat (limited to 'src/zenhttp/servers/httpsys.cpp')
-rw-r--r--src/zenhttp/servers/httpsys.cpp53
1 files changed, 33 insertions, 20 deletions
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index c640ba90b..6809c280a 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -128,6 +128,7 @@ private:
bool m_IsAsyncResponseEnabled = true;
std::unique_ptr<WinIoThreadPool> m_IoThreadPool;
+ bool m_IoThreadPoolIsWinTp = true;
RwLock m_AsyncWorkPoolInitLock;
WorkerThreadPool* m_AsyncWorkPool = nullptr;
@@ -374,7 +375,8 @@ public:
void IssueInitialRequest(std::error_code& ErrorCode);
bool IssueNextRequest(HttpSysRequestHandler* NewCompletionHandler);
- PTP_IO Iocp();
+ void StartIo();
+ void CancelIo();
HANDLE RequestQueueHandle();
inline OVERLAPPED* Overlapped() { return &m_HttpOverlapped; }
inline HttpSysServer& Server() { return m_HttpServer; }
@@ -614,9 +616,8 @@ HttpMessageResponseRequest::IssueRequest(std::error_code& ErrorCode)
ZEN_TRACE_CPU("httpsys::Response::IssueRequest");
HttpSysTransaction& Tx = Transaction();
HTTP_REQUEST* const HttpReq = Tx.HttpRequest();
- PTP_IO const Iocp = Tx.Iocp();
- StartThreadpoolIo(Iocp);
+ Tx.StartIo();
// Split payload into batches to play well with the underlying API
@@ -822,7 +823,7 @@ HttpMessageResponseRequest::IssueRequest(std::error_code& ErrorCode)
// An error occurred, no completion will be posted to IOCP
- CancelThreadpoolIo(Iocp);
+ Tx.CancelIo();
// Emit diagnostics
@@ -1001,7 +1002,8 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig)
MaxThreadCount *= 2;
}
- m_IoThreadPool = std::make_unique<WinIoThreadPool>(MinThreadCount, MaxThreadCount);
+ m_IoThreadPoolIsWinTp = !m_InitialConfig.UseExplicitIoThreadPool;
+ m_IoThreadPool = WinIoThreadPool::Create(!m_IoThreadPoolIsWinTp, MinThreadCount, MaxThreadCount);
if (m_InitialConfig.AsyncWorkThreadCount == 0)
{
@@ -1018,10 +1020,11 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig)
m_IsHttpInitialized = true;
m_IsOk = true;
- ZEN_INFO("http.sys server started in {} mode, using {}-{} I/O threads and {} async worker threads",
+ 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.UseExplicitIoThreadPool ? "explicit IOCP" : "Windows Thread Pool",
m_InitialConfig.AsyncWorkThreadCount);
}
@@ -1497,10 +1500,16 @@ HttpSysTransaction::~HttpSysTransaction()
{
}
-PTP_IO
-HttpSysTransaction::Iocp()
+void
+HttpSysTransaction::StartIo()
+{
+ m_HttpServer.m_IoThreadPool->StartIo();
+}
+
+void
+HttpSysTransaction::CancelIo()
{
- return m_HttpServer.m_IoThreadPool->Iocp();
+ m_HttpServer.m_IoThreadPool->CancelIo();
}
HANDLE
@@ -1521,7 +1530,6 @@ static std::atomic<int> HttpSysThreadIndex = 0;
static void
NameCurrentHttpSysThread()
{
- t_IsHttpSysThreadNamed = true;
const int ThreadIndex = ++HttpSysThreadIndex;
zen::ExtendableStringBuilder<16> ThreadName;
ThreadName << "httpio_" << ThreadIndex;
@@ -1538,19 +1546,25 @@ HttpSysTransaction::IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance,
{
ZEN_UNUSED(Io, Instance);
- // Assign names to threads for context
-
- if (!t_IsHttpSysThreadNamed)
- {
- NameCurrentHttpSysThread();
- }
-
// Note that for a given transaction we may be in this completion function on more
// than one thread at any given moment. This means we need to be careful about what
// happens in here
HttpSysTransaction* Transaction = CONTAINING_RECORD(pOverlapped, HttpSysTransaction, m_HttpOverlapped);
+ // Assign names to threads for context (only needed when using Windows' native
+ // thread pool)
+
+ if (Transaction->Server().m_IoThreadPoolIsWinTp)
+ {
+ if (!t_IsHttpSysThreadNamed)
+ {
+ t_IsHttpSysThreadNamed = true;
+
+ NameCurrentHttpSysThread();
+ }
+ }
+
if (Transaction->HandleCompletion(IoResult, NumberOfBytesTransferred) == HttpSysTransaction::Status::kDone)
{
delete Transaction;
@@ -2020,10 +2034,9 @@ InitialRequestHandler::IssueRequest(std::error_code& ErrorCode)
ZEN_TRACE_CPU("httpsys::Request::IssueRequest");
HttpSysTransaction& Tx = Transaction();
- PTP_IO Iocp = Tx.Iocp();
HTTP_REQUEST* HttpReq = Tx.HttpRequest();
- StartThreadpoolIo(Iocp);
+ Tx.StartIo();
ULONG HttpApiResult;
@@ -2057,7 +2070,7 @@ InitialRequestHandler::IssueRequest(std::error_code& ErrorCode)
if (HttpApiResult != ERROR_IO_PENDING && HttpApiResult != NO_ERROR)
{
- CancelThreadpoolIo(Iocp);
+ Tx.CancelIo();
ErrorCode = MakeErrorCode(HttpApiResult);