From c11dff8639e64168a0ea12d69fe8baa4a74ebaf2 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Sat, 17 Dec 2022 14:34:03 -0500
Subject: [PATCH] EmuThread: refactor

---
 src/core/core.cpp        | 18 +++------
 src/core/core.h          |  4 +-
 src/yuzu/bootmanager.cpp | 82 ++++++++++++++--------------------------
 src/yuzu/bootmanager.h   | 51 ++++++++++++-------------
 src/yuzu/main.cpp        | 80 ++-------------------------------------
 src/yuzu/main.h          |  1 -
 6 files changed, 64 insertions(+), 172 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index a738f221ff..47292cd78f 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -183,26 +183,20 @@ struct System::Impl {
         Initialize(system);
     }
 
-    SystemResultStatus Run() {
+    void Run() {
         std::unique_lock<std::mutex> lk(suspend_guard);
-        status = SystemResultStatus::Success;
 
         kernel.Suspend(false);
         core_timing.SyncPause(false);
         is_paused.store(false, std::memory_order_relaxed);
-
-        return status;
     }
 
-    SystemResultStatus Pause() {
+    void Pause() {
         std::unique_lock<std::mutex> lk(suspend_guard);
-        status = SystemResultStatus::Success;
 
         core_timing.SyncPause(true);
         kernel.Suspend(true);
         is_paused.store(true, std::memory_order_relaxed);
-
-        return status;
     }
 
     bool IsPaused() const {
@@ -553,12 +547,12 @@ void System::Initialize() {
     impl->Initialize(*this);
 }
 
-SystemResultStatus System::Run() {
-    return impl->Run();
+void System::Run() {
+    impl->Run();
 }
 
-SystemResultStatus System::Pause() {
-    return impl->Pause();
+void System::Pause() {
+    impl->Pause();
 }
 
 bool System::IsPaused() const {
diff --git a/src/core/core.h b/src/core/core.h
index 4ebedffd91..fb5cda2f56 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -152,13 +152,13 @@ public:
      * Run the OS and Application
      * This function will start emulation and run the relevant devices
      */
-    [[nodiscard]] SystemResultStatus Run();
+    void Run();
 
     /**
      * Pause the OS and Application
      * This function will pause emulation and stop the relevant devices
      */
-    [[nodiscard]] SystemResultStatus Pause();
+    void Pause();
 
     /// Check if the core is currently paused.
     [[nodiscard]] bool IsPaused() const;
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 682b37f47b..40b3d91fcc 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -46,30 +46,28 @@
 
 static Core::Frontend::WindowSystemType GetWindowSystemType();
 
-EmuThread::EmuThread(Core::System& system_) : system{system_} {}
+EmuThread::EmuThread(Core::System& system) : m_system{system} {}
 
 EmuThread::~EmuThread() = default;
 
 void EmuThread::run() {
-    std::string name = "EmuControlThread";
-    MicroProfileOnThreadCreate(name.c_str());
-    Common::SetCurrentThreadName(name.c_str());
+    const char* name = "EmuControlThread";
+    MicroProfileOnThreadCreate(name);
+    Common::SetCurrentThreadName(name);
 
-    auto& gpu = system.GPU();
-    auto stop_token = stop_source.get_token();
-    bool debugger_should_start = system.DebuggerEnabled();
+    auto& gpu = m_system.GPU();
+    auto stop_token = m_stop_source.get_token();
 
-    system.RegisterHostThread();
+    m_system.RegisterHostThread();
 
     // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
     // execution.
     gpu.ObtainContext();
 
     emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
-
     if (Settings::values.use_disk_shader_cache.GetValue()) {
-        system.Renderer().ReadRasterizer()->LoadDiskResources(
-            system.GetCurrentProcessProgramID(), stop_token,
+        m_system.Renderer().ReadRasterizer()->LoadDiskResources(
+            m_system.GetCurrentProcessProgramID(), stop_token,
             [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
                 emit LoadProgress(stage, value, total);
             });
@@ -79,57 +77,35 @@ void EmuThread::run() {
     gpu.ReleaseContext();
     gpu.Start();
 
-    system.GetCpuManager().OnGpuReady();
+    m_system.GetCpuManager().OnGpuReady();
+    m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
 
-    system.RegisterExitCallback([this]() {
-        stop_source.request_stop();
-        SetRunning(false);
-    });
+    if (m_system.DebuggerEnabled()) {
+        m_system.InitializeDebugger();
+    }
 
-    // Holds whether the cpu was running during the last iteration,
-    // so that the DebugModeLeft signal can be emitted before the
-    // next execution step
-    bool was_active = false;
     while (!stop_token.stop_requested()) {
-        if (running) {
-            if (was_active) {
-                emit DebugModeLeft();
-            }
+        std::unique_lock lk{m_should_run_mutex};
+        if (m_should_run) {
+            m_system.Run();
+            m_is_running.store(true);
+            m_is_running.notify_all();
 
-            running_guard = true;
-            Core::SystemResultStatus result = system.Run();
-            if (result != Core::SystemResultStatus::Success) {
-                running_guard = false;
-                this->SetRunning(false);
-                emit ErrorThrown(result, system.GetStatusDetails());
-            }
-
-            if (debugger_should_start) {
-                system.InitializeDebugger();
-                debugger_should_start = false;
-            }
-
-            running_wait.Wait();
-            result = system.Pause();
-            if (result != Core::SystemResultStatus::Success) {
-                running_guard = false;
-                this->SetRunning(false);
-                emit ErrorThrown(result, system.GetStatusDetails());
-            }
-            running_guard = false;
-
-            if (!stop_token.stop_requested()) {
-                was_active = true;
-                emit DebugModeEntered();
-            }
+            Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
         } else {
-            std::unique_lock lock{running_mutex};
-            Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
+            m_system.Pause();
+            m_is_running.store(false);
+            m_is_running.notify_all();
+
+            emit DebugModeEntered();
+            Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
+            emit DebugModeLeft();
         }
     }
 
     // Shutdown the main emulated process
-    system.ShutdownMainProcess();
+    m_system.DetachDebugger();
+    m_system.ShutdownMainProcess();
 
 #if MICROPROFILE_ENABLED
     MicroProfileOnThreadExit();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 5bbcf61f74..52867d628b 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -47,7 +47,7 @@ class EmuThread final : public QThread {
     Q_OBJECT
 
 public:
-    explicit EmuThread(Core::System& system_);
+    explicit EmuThread(Core::System& system);
     ~EmuThread() override;
 
     /**
@@ -57,30 +57,30 @@ public:
     void run() override;
 
     /**
-     * Sets whether the emulation thread is running or not
-     * @param running_ Boolean value, set the emulation thread to running if true
-     * @note This function is thread-safe
+     * Sets whether the emulation thread should run or not
+     * @param should_run Boolean value, set the emulation thread to running if true
      */
-    void SetRunning(bool running_) {
-        std::unique_lock lock{running_mutex};
-        running = running_;
-        lock.unlock();
-        running_cv.notify_all();
-        if (!running) {
-            running_wait.Set();
-            /// Wait until effectively paused
-            while (running_guard)
-                ;
+    void SetRunning(bool should_run) {
+        // TODO: Prevent other threads from modifying the state until we finish.
+        {
+            // Notify the running thread to change state.
+            std::unique_lock run_lk{m_should_run_mutex};
+            m_should_run = should_run;
+            m_should_run_cv.notify_one();
+        }
+
+        // Wait until paused, if pausing.
+        if (!should_run) {
+            m_is_running.wait(true);
         }
     }
 
     /**
      * Check if the emulation thread is running or not
      * @return True if the emulation thread is running, otherwise false
-     * @note This function is thread-safe
      */
     bool IsRunning() const {
-        return running;
+        return m_is_running.load();
     }
 
     /**
@@ -88,18 +88,17 @@ public:
      */
     void ForceStop() {
         LOG_WARNING(Frontend, "Force stopping EmuThread");
-        stop_source.request_stop();
-        SetRunning(false);
+        m_stop_source.request_stop();
     }
 
 private:
-    bool running = false;
-    std::stop_source stop_source;
-    std::mutex running_mutex;
-    std::condition_variable_any running_cv;
-    Common::Event running_wait{};
-    std::atomic_bool running_guard{false};
-    Core::System& system;
+    Core::System& m_system;
+
+    std::stop_source m_stop_source;
+    std::mutex m_should_run_mutex;
+    std::condition_variable_any m_should_run_cv;
+    std::atomic<bool> m_is_running{false};
+    bool m_should_run{true};
 
 signals:
     /**
@@ -120,8 +119,6 @@ signals:
      */
     void DebugModeLeft();
 
-    void ErrorThrown(Core::SystemResultStatus, std::string);
-
     void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
 };
 
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2e6c2311ac..c5285ffc96 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1794,15 +1794,16 @@ void GMainWindow::ShutdownGame() {
     Settings::values.use_speed_limit.SetValue(true);
 
     system->SetShuttingDown(true);
-    system->DetachDebugger();
     discord_rpc->Pause();
 
     RequestGameExit();
+    emu_thread->disconnect();
+    emu_thread->SetRunning(true);
 
     emit EmulationStopping();
 
     // Wait for emulation thread to complete and delete it
-    if (!emu_thread->wait(5000)) {
+    if (system->DebuggerEnabled() || !emu_thread->wait(5000)) {
         emu_thread->ForceStop();
         emu_thread->wait();
     }
@@ -2916,8 +2917,6 @@ void GMainWindow::OnStartGame() {
 
     emu_thread->SetRunning(true);
 
-    connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
-
     UpdateMenuState();
     OnTasStateChanged();
 
@@ -3901,79 +3900,6 @@ void GMainWindow::OnMouseActivity() {
     mouse_center_timer.stop();
 }
 
-void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
-    QMessageBox::StandardButton answer;
-    QString status_message;
-    const QString common_message =
-        tr("The game you are trying to load requires additional files from your Switch to be "
-           "dumped "
-           "before playing.<br/><br/>For more information on dumping these files, please see the "
-           "following wiki page: <a "
-           "href='https://yuzu-emu.org/wiki/"
-           "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
-           "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
-           "quit "
-           "back to the game list? Continuing emulation may result in crashes, corrupted save "
-           "data, or other bugs.");
-    switch (result) {
-    case Core::SystemResultStatus::ErrorSystemFiles: {
-        QString message;
-        if (details.empty()) {
-            message =
-                tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message);
-        } else {
-            message = tr("yuzu was unable to locate a Switch system archive: %1. %2")
-                          .arg(QString::fromStdString(details), common_message);
-        }
-
-        answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
-                                       QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
-        status_message = tr("System Archive Missing");
-        break;
-    }
-
-    case Core::SystemResultStatus::ErrorSharedFont: {
-        const QString message =
-            tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message);
-        answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
-                                       QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
-        status_message = tr("Shared Font Missing");
-        break;
-    }
-
-    default:
-        answer = QMessageBox::question(
-            this, tr("Fatal Error"),
-            tr("yuzu has encountered a fatal error, please see the log for more details. "
-               "For more information on accessing the log, please see the following page: "
-               "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
-               "to "
-               "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
-               "list? "
-               "Continuing emulation may result in crashes, corrupted save data, or other "
-               "bugs."),
-            QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
-        status_message = tr("Fatal Error encountered");
-        break;
-    }
-
-    if (answer == QMessageBox::Yes) {
-        if (emu_thread) {
-            ShutdownGame();
-
-            Settings::RestoreGlobalState(system->IsPoweredOn());
-            system->HIDCore().ReloadInputDevices();
-            UpdateStatusButtons();
-        }
-    } else {
-        // Only show the message if the game is still running.
-        if (emu_thread) {
-            emu_thread->SetRunning(true);
-            message_label->setText(status_message);
-        }
-    }
-}
-
 void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
     if (behavior == ReinitializeKeyBehavior::Warning) {
         const auto res = QMessageBox::information(
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1047ba276e..5b84c7a007 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -332,7 +332,6 @@ private slots:
     void ResetWindowSize900();
     void ResetWindowSize1080();
     void OnCaptureScreenshot();
-    void OnCoreError(Core::SystemResultStatus, std::string);
     void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
     void OnLanguageChanged(const QString& locale);
     void OnMouseActivity();