diff options
| author | Stefan Boberg <[email protected]> | 2026-03-15 20:56:12 +0100 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2026-03-15 20:56:12 +0100 |
| commit | 5e487e28988f5dec8acb1e3be51afc2ebfdc1374 (patch) | |
| tree | 6880f99ef442e51167e1245b997910d261bf8cc9 /src/zenhttp | |
| parent | Merge remote-tracking branch 'origin/main' into sb/threadpool (diff) | |
| download | zen-5e487e28988f5dec8acb1e3be51afc2ebfdc1374.tar.xz zen-5e487e28988f5dec8acb1e3be51afc2ebfdc1374.zip | |
Fix thread vector growth in dynamic thread pools
Exiting threads now register their ID before returning, and
PruneExitedThreads() joins and removes them on the next scale-up,
preventing unbounded vector growth through repeated scale cycles.
Diffstat (limited to 'src/zenhttp')
| -rw-r--r-- | src/zenhttp/servers/iothreadpool.cpp | 32 | ||||
| -rw-r--r-- | src/zenhttp/servers/iothreadpool.h | 6 |
2 files changed, 36 insertions, 2 deletions
diff --git a/src/zenhttp/servers/iothreadpool.cpp b/src/zenhttp/servers/iothreadpool.cpp index d180f17f8..1053f0b0c 100644 --- a/src/zenhttp/servers/iothreadpool.cpp +++ b/src/zenhttp/servers/iothreadpool.cpp @@ -8,6 +8,7 @@ #if ZEN_PLATFORM_WINDOWS +# include <algorithm> # include <thread> namespace zen { @@ -187,10 +188,36 @@ ExplicitIoThreadPool::CancelIo() } void +ExplicitIoThreadPool::PruneExitedThreads() +{ + // Must be called under m_ThreadListLock + if (m_ExitedThreadIds.empty()) + { + return; + } + + for (auto It = m_Threads.begin(); It != m_Threads.end();) + { + auto IdIt = std::find(m_ExitedThreadIds.begin(), m_ExitedThreadIds.end(), It->get_id()); + if (IdIt != m_ExitedThreadIds.end()) + { + It->join(); + It = m_Threads.erase(It); + m_ExitedThreadIds.erase(IdIt); + } + else + { + ++It; + } + } +} + +void ExplicitIoThreadPool::SpawnWorkerThread() { RwLock::ExclusiveLockScope _(m_ThreadListLock); + PruneExitedThreads(); ++m_TotalThreads; m_Threads.emplace_back([this] { WorkerThreadMain(); }); } @@ -237,6 +264,10 @@ ExplicitIoThreadPool::WorkerThreadMain() ZEN_LOG_DEBUG(ExplicitIoPoolLog(), "scaling down I/O thread (idle timeout), {} threads remaining", CurrentTotal - 1); + { + RwLock::ExclusiveLockScope _(m_ThreadListLock); + m_ExitedThreadIds.push_back(std::this_thread::get_id()); + } return; // Thread exits } } @@ -278,6 +309,7 @@ ExplicitIoThreadPool::WorkerThreadMain() // We already incremented m_TotalThreads, so do the actual spawn { RwLock::ExclusiveLockScope _(m_ThreadListLock); + PruneExitedThreads(); m_Threads.emplace_back([this] { WorkerThreadMain(); }); } } diff --git a/src/zenhttp/servers/iothreadpool.h b/src/zenhttp/servers/iothreadpool.h index e2c15ba76..f6bfce450 100644 --- a/src/zenhttp/servers/iothreadpool.h +++ b/src/zenhttp/servers/iothreadpool.h @@ -92,6 +92,7 @@ public: private: void WorkerThreadMain(); void SpawnWorkerThread(); + void PruneExitedThreads(); HANDLE m_Iocp = nullptr; @@ -105,8 +106,9 @@ private: std::atomic<int> m_ActiveCount{0}; std::atomic<bool> m_ShuttingDown{false}; - RwLock m_ThreadListLock; - std::vector<std::thread> m_Threads; + RwLock m_ThreadListLock; + std::vector<std::thread> m_Threads; + std::vector<std::thread::id> m_ExitedThreadIds; }; } // namespace zen |