From f44c60321ec767b6e881160d4960345d678edf78 Mon Sep 17 00:00:00 2001
From: Gus Caplan <me@gus.host>
Date: Mon, 18 Jul 2022 00:41:29 -0700
Subject: [PATCH 1/2] let games gracefully exit

---
 src/yuzu/bootmanager.cpp |  5 +++++
 src/yuzu/bootmanager.h   | 10 ++++++++++
 src/yuzu/main.cpp        |  5 ++---
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5b5b6fed86..f7321258c2 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -80,6 +80,11 @@ void EmuThread::run() {
 
     system.GetCpuManager().OnGpuReady();
 
+    system.RegisterExitCallback([this]() {
+        stop_source.request_stop();
+        SetRunning(false);
+    });
+
     // Holds whether the cpu was running during the last iteration,
     // so that the DebugModeLeft signal can be emitted before the
     // next execution step
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index f4deae4ee3..c2b127f192 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -11,6 +11,7 @@
 #include <QImage>
 #include <QStringList>
 #include <QThread>
+#include <QTimer>
 #include <QTouchEvent>
 #include <QWidget>
 
@@ -87,6 +88,15 @@ public:
      * Requests for the emulation thread to stop running
      */
     void RequestStop() {
+        QTimer::singleShot(5000, this, &EmuThread::ForceStop);
+    }
+
+private slots:
+    void ForceStop() {
+        if (!IsRunning()) {
+            return;
+        }
+        LOG_WARNING(Frontend, "Force stopping EmuThread");
         stop_source.request_stop();
         SetRunning(false);
     }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b11b26f7b4..b38957d9a2 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1707,9 +1707,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
     system->RegisterExecuteProgramCallback(
         [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
 
-    // Register an Exit callback such that Core can exit the currently running application.
-    system->RegisterExitCallback([this]() { render_window->Exit(); });
-
     connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
     connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
     // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
@@ -1793,6 +1790,8 @@ void GMainWindow::ShutdownGame() {
     system->SetShuttingDown(true);
     system->DetachDebugger();
     discord_rpc->Pause();
+
+    RequestGameExit();
     emu_thread->RequestStop();
 
     emit EmulationStopping();

From 179adee396e6b8480d52be5314979830beebfbc1 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Mon, 12 Dec 2022 21:38:20 -0500
Subject: [PATCH 2/2] emu_thread: properly force shutdown for unresponsive
 guest programs

---
 src/yuzu/bootmanager.h | 11 +----------
 src/yuzu/main.cpp      |  6 ++++--
 2 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index c2b127f192..f0edad6e4c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -11,7 +11,6 @@
 #include <QImage>
 #include <QStringList>
 #include <QThread>
-#include <QTimer>
 #include <QTouchEvent>
 #include <QWidget>
 
@@ -85,17 +84,9 @@ public:
     }
 
     /**
-     * Requests for the emulation thread to stop running
+     * Requests for the emulation thread to immediately stop running
      */
-    void RequestStop() {
-        QTimer::singleShot(5000, this, &EmuThread::ForceStop);
-    }
-
-private slots:
     void ForceStop() {
-        if (!IsRunning()) {
-            return;
-        }
         LOG_WARNING(Frontend, "Force stopping EmuThread");
         stop_source.request_stop();
         SetRunning(false);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b38957d9a2..b3ae03eaf5 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1792,12 +1792,14 @@ void GMainWindow::ShutdownGame() {
     discord_rpc->Pause();
 
     RequestGameExit();
-    emu_thread->RequestStop();
 
     emit EmulationStopping();
 
     // Wait for emulation thread to complete and delete it
-    emu_thread->wait();
+    if (!emu_thread->wait(5000)) {
+        emu_thread->ForceStop();
+        emu_thread->wait();
+    }
     emu_thread = nullptr;
 
     emulation_running = false;