diff options
| author | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 11:22:23 -0500 |
|---|---|---|
| committer | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 14:53:47 -0500 |
| commit | a3d67c14c86f57ec12787db0b8bb058ec8b5f73c (patch) | |
| tree | ba9d1d9396927fc6d3d9bf481ffc9f24b5aa24a5 /src/yuzu/main_window.cpp | |
| parent | c83eb1d1f5c14d0842a165de431ac6367dff2909 (diff) | |
frontend: bound the wait for the emulation thread at shutdown
426f120ee8 dropped the 10 s wait and terminate() fallback when stopping
emulation, and ShutdownGame and closeEvent now wait for the emulation
thread with no limit. If the thread hangs (for example a GPU or present
deadlock), stopping a game, switching games or quitting never completes.
Keep the asynchronous teardown, but once the thread has been asked to
stop, give it 10 s and then terminate it and finish cleanup, as before.
ShutdownGame's nested event loop gets the same bound. A terminated
thread is logged as an error.
Assisted-by: Claude Sonnet 5.5 (implementation and commit message)
Diffstat (limited to 'src/yuzu/main_window.cpp')
| -rw-r--r-- | src/yuzu/main_window.cpp | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index e5a8f81424..02b36cf9cf 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -320,6 +320,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; constexpr int default_mouse_hide_timeout = 2500; constexpr int default_input_update_timeout = 1; +constexpr int EmuThreadStopTimeoutMs = 10'000; constexpr size_t CopyBufferSize = 1_MiB; @@ -2209,6 +2210,23 @@ void MainWindow::OnShutdownBeginDialog() { void MainWindow::OnEmulationStopTimeExpired() { if (QtCommon::emu_thread) { QtCommon::emu_thread->ForceStop(); + emu_terminate_timer.setSingleShot(true); + emu_terminate_timer.start(EmuThreadStopTimeoutMs); + connect(&emu_terminate_timer, &QTimer::timeout, this, &MainWindow::OnEmulationStopTimeout, + Qt::UniqueConnection); + } +} + +void MainWindow::OnEmulationStopTimeout() { + TerminateEmulationThread(); + OnEmulationStopped(); +} + +void MainWindow::TerminateEmulationThread() { + if (QtCommon::emu_thread && QtCommon::emu_thread->isRunning()) { + LOG_ERROR(Frontend, "Emulation thread failed to stop, terminating it"); + QtCommon::emu_thread->terminate(); + QtCommon::emu_thread->wait(); } } @@ -2220,6 +2238,7 @@ void MainWindow::OnEmulationStopped() { return; } shutdown_timer.stop(); + emu_terminate_timer.stop(); if (QtCommon::emu_thread) { QtCommon::emu_thread->disconnect(); QtCommon::emu_thread->wait(); @@ -2309,13 +2328,17 @@ void MainWindow::ShutdownGame() { play_time_manager->Stop(); OnShutdownBegin(); OnEmulationStopTimeExpired(); - if (QtCommon::emu_thread) { + emu_terminate_timer.stop(); + if (QtCommon::emu_thread && QtCommon::emu_thread->isRunning()) { QEventLoop loop; + QTimer timeout; + timeout.setSingleShot(true); connect(QtCommon::emu_thread.get(), &QThread::finished, &loop, &QEventLoop::quit); - if (QtCommon::emu_thread->isRunning()) { - loop.exec(QEventLoop::ExcludeUserInputEvents); - } + connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); + timeout.start(EmuThreadStopTimeoutMs); + loop.exec(QEventLoop::ExcludeUserInputEvents); } + TerminateEmulationThread(); OnEmulationStopped(); } |
