From 2968debfe75acfa45c8498a61c673eacb6534358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 17 Oct 2026 13:51:56 +0200 Subject: [PATCH] Windows: Don't hang on exit when a HID pad stops sending reports DualShock, DualSense and Switch Pro pads are read with a blocking ReadFile on the input thread, and WM_DESTROY joins that thread. If the pad stops sending reports the read never returns, so the window is gone but the process stays. Cancel the thread's I/O until it stops, retrying in case a cancel lands just before a read starts. This also covers the synchronous writes the pads' shutdown does. Co-Authored-By: Claude Opus 5.5 (1M context) --- Windows/InputDevice.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Windows/InputDevice.cpp b/Windows/InputDevice.cpp index 0b9c56d328..f9e638365e 100644 --- a/Windows/InputDevice.cpp +++ b/Windows/InputDevice.cpp @@ -83,8 +83,23 @@ void InputManager::BeginPolling() { } void InputManager::StopPolling() { + INFO_LOG(Log::System, "Stopping the input thread"); runThread_.store(false, std::memory_order_relaxed); +#if !PPSSPP_PLATFORM(UWP) + // The HID pads are read with a blocking ReadFile, which never returns if the pad stops sending + // reports, and then the join below waits forever with the window already gone. Cancel whatever + // I/O the thread is blocked in. Repeat it, since a cancel that lands just before a read starts + // does nothing. + HANDLE thread = (HANDLE)inputThread_.native_handle(); + for (int tries = 0; WaitForSingleObject(thread, 100) == WAIT_TIMEOUT; tries++) { + if (tries == 0) { + WARN_LOG(Log::System, "Input thread didn't stop, cancelling its I/O"); + } + CancelSynchronousIo(thread); + } +#endif inputThread_.join(); + INFO_LOG(Log::System, "Input thread stopped"); } bool InputManager::AnyAccelerometer() const {