aboutsummaryrefslogtreecommitdiff
path: root/src/yuzu/main_window.cpp
diff options
context:
space:
mode:
authorSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 11:22:23 -0500
committerSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 14:53:47 -0500
commita3d67c14c86f57ec12787db0b8bb058ec8b5f73c (patch)
treeba9d1d9396927fc6d3d9bf481ffc9f24b5aa24a5 /src/yuzu/main_window.cpp
parentc83eb1d1f5c14d0842a165de431ac6367dff2909 (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.cpp31
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();
}