From 1d64bc97108326a9b4dce9b0c2cb0cec81bc6766 Mon Sep 17 00:00:00 2001 From: 0blu Date: Tue, 12 Nov 2024 22:14:38 +0100 Subject: [PATCH] Native,Windows: Fix shutdown `IoContext` stuck bug Some threads were hanging on windows xp when the `IO::IoContext` was shutting down --- src/shared/IO/Context/IoContext.h | 1 + src/shared/IO/Context/IoContext_windows.cpp | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/shared/IO/Context/IoContext.h b/src/shared/IO/Context/IoContext.h index 5e8175bdfe0..eb74f831c18 100644 --- a/src/shared/IO/Context/IoContext.h +++ b/src/shared/IO/Context/IoContext.h @@ -69,6 +69,7 @@ namespace IO #if defined(WIN32) explicit IoContext(HANDLE completionPort); HANDLE m_completionPort; + volatile uint32_t m_runningThreadsCount; #elif defined(__linux__) IO::Native::FileHandle const m_epollDescriptor; IO::Native::FileHandle const m_contextSwitchNotifyEventFd; diff --git a/src/shared/IO/Context/IoContext_windows.cpp b/src/shared/IO/Context/IoContext_windows.cpp index 3d74d3dde45..c835f0800c9 100644 --- a/src/shared/IO/Context/IoContext_windows.cpp +++ b/src/shared/IO/Context/IoContext_windows.cpp @@ -15,7 +15,7 @@ std::unique_ptr IO::IoContext::CreateIoContext() return std::unique_ptr(new IoContext(completionPort)); } -IO::IoContext::IoContext(HANDLE completionPort) : m_completionPort(completionPort), m_isRunning{true} +IO::IoContext::IoContext(HANDLE completionPort) : m_isRunning(true), m_completionPort(completionPort), m_runningThreadsCount(0) { } @@ -35,6 +35,7 @@ void IO::IoContext::RunUntilShutdown() DWORD bytesWritten = 0; DWORD constexpr maxWait = INFINITE; + m_runningThreadsCount++; while (m_isRunning) { bool isOkay = ::GetQueuedCompletionStatus(m_completionPort, &bytesWritten, &completionKey, reinterpret_cast(&task), maxWait); @@ -53,6 +54,7 @@ void IO::IoContext::RunUntilShutdown() std::this_thread::yield(); // wait one os tick to try again } } + m_runningThreadsCount--; } bool IO::IoContext::IsRunning() const @@ -64,7 +66,19 @@ void IO::IoContext::Shutdown() { if (m_isRunning) { + uint32_t runningThreadsCountLocal = m_runningThreadsCount; // local count to prevent race condition after `running = false` m_isRunning = false; + + // We need to wake up the running threads by sending a "null-completion-event" and wait until all thread stopped + for (uint32_t i = 0; i < runningThreadsCountLocal; i++) + { + ::PostQueuedCompletionStatus(m_completionPort, 0, 0, nullptr); + } + while (m_runningThreadsCount > 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + ::CloseHandle(m_completionPort); m_completionPort = nullptr; }