Преглед изворни кода

core/debugger: fix a number of shutdown deadlocks

Liam пре 4 година
родитељ
комит
1f0fee33ed

+ 6 - 0
src/core/core.cpp

@@ -493,6 +493,12 @@ void System::Shutdown() {
     impl->Shutdown();
 }
 
+void System::DetachDebugger() {
+    if (impl->debugger) {
+        impl->debugger->NotifyShutdown();
+    }
+}
+
 std::unique_lock<std::mutex> System::StallCPU() {
     return impl->StallCPU();
 }

+ 3 - 0
src/core/core.h

@@ -160,6 +160,9 @@ public:
     /// Shutdown the emulated system.
     void Shutdown();
 
+    /// Forcibly detach the debugger if it is running.
+    void DetachDebugger();
+
     std::unique_lock<std::mutex> StallCPU();
     void UnstallCPU();
 

+ 48 - 7
src/core/debugger/debugger.cpp

@@ -42,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
     return received_data;
 }
 
+enum class SignalType {
+    Stopped,
+    ShuttingDown,
+};
+
+struct SignalInfo {
+    SignalType type;
+    Kernel::KThread* thread;
+};
+
 namespace Core {
 
 class DebuggerImpl : public DebuggerBackend {
@@ -56,7 +66,7 @@ public:
         ShutdownServer();
     }
 
-    bool NotifyThreadStopped(Kernel::KThread* thread) {
+    bool SignalDebugger(SignalInfo signal_info) {
         std::scoped_lock lk{connection_lock};
 
         if (stopped) {
@@ -64,9 +74,13 @@ public:
             // It should be ignored.
             return false;
         }
+
+        // Set up the state.
         stopped = true;
+        info = signal_info;
 
-        boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
+        // Write a single byte into the pipe to wake up the debug interface.
+        boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
         return true;
     }
 
@@ -124,7 +138,7 @@ private:
         Common::SetCurrentThreadName("yuzu:Debugger");
 
         // Set up the client signals for new data.
-        AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
+        AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
         AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
 
         // Stop the emulated CPU.
@@ -142,9 +156,28 @@ private:
     }
 
     void PipeData(std::span<const u8> data) {
-        AllCoreStop();
-        UpdateActiveThread();
-        frontend->Stopped(active_thread);
+        switch (info.type) {
+        case SignalType::Stopped:
+            // Stop emulation.
+            AllCoreStop();
+
+            // Notify the client.
+            active_thread = info.thread;
+            UpdateActiveThread();
+            frontend->Stopped(active_thread);
+
+            break;
+        case SignalType::ShuttingDown:
+            frontend->ShuttingDown();
+
+            // Wait for emulation to shut down gracefully now.
+            suspend.reset();
+            signal_pipe.close();
+            client_socket.shutdown(boost::asio::socket_base::shutdown_both);
+            LOG_INFO(Debug_GDBStub, "Shut down server");
+
+            break;
+        }
     }
 
     void ClientData(std::span<const u8> data) {
@@ -246,7 +279,9 @@ private:
     boost::asio::ip::tcp::socket client_socket;
     std::optional<std::unique_lock<std::mutex>> suspend;
 
+    SignalInfo info;
     Kernel::KThread* active_thread;
+    bool pipe_data;
     bool stopped;
 
     std::array<u8, 4096> client_data;
@@ -263,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) {
 Debugger::~Debugger() = default;
 
 bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
-    return impl && impl->NotifyThreadStopped(thread);
+    return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
+}
+
+void Debugger::NotifyShutdown() {
+    if (impl) {
+        impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
+    }
 }
 
 } // namespace Core

+ 5 - 0
src/core/debugger/debugger.h

@@ -35,6 +35,11 @@ public:
      */
     bool NotifyThreadStopped(Kernel::KThread* thread);
 
+    /**
+     * Notify the debugger that a shutdown is being performed now and disconnect.
+     */
+    void NotifyShutdown();
+
 private:
     std::unique_ptr<DebuggerImpl> impl;
 };

+ 5 - 0
src/core/debugger/debugger_interface.h

@@ -66,6 +66,11 @@ public:
      */
     virtual void Stopped(Kernel::KThread* thread) = 0;
 
+    /**
+     * Called when emulation is shutting down.
+     */
+    virtual void ShuttingDown() = 0;
+
     /**
      * Called when new data is asynchronously received on the client socket.
      * A list of actions to perform is returned.

+ 2 - 0
src/core/debugger/gdbstub.cpp

@@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default;
 
 void GDBStub::Connected() {}
 
+void GDBStub::ShuttingDown() {}
+
 void GDBStub::Stopped(Kernel::KThread* thread) {
     SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
 }

+ 1 - 0
src/core/debugger/gdbstub.h

@@ -23,6 +23,7 @@ public:
 
     void Connected() override;
     void Stopped(Kernel::KThread* thread) override;
+    void ShuttingDown() override;
     std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
 
 private:

+ 1 - 0
src/yuzu/main.cpp

@@ -1545,6 +1545,7 @@ void GMainWindow::ShutdownGame() {
 
     AllowOSSleep();
 
+    system->DetachDebugger();
     discord_rpc->Pause();
     emu_thread->RequestStop();
 

+ 1 - 0
src/yuzu_cmd/yuzu.cpp

@@ -229,6 +229,7 @@ int main(int argc, char** argv) {
     while (emu_window->IsOpen()) {
         emu_window->WaitEvent();
     }
+    system.DetachDebugger();
     void(system.Pause());
     system.Shutdown();