core/debugger: fix a number of shutdown deadlocks
This commit is contained in:
parent
de6c0defb3
commit
1f0fee33ed
|
@ -493,6 +493,12 @@ void System::Shutdown() {
|
||||||
impl->Shutdown();
|
impl->Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::DetachDebugger() {
|
||||||
|
if (impl->debugger) {
|
||||||
|
impl->debugger->NotifyShutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_lock<std::mutex> System::StallCPU() {
|
std::unique_lock<std::mutex> System::StallCPU() {
|
||||||
return impl->StallCPU();
|
return impl->StallCPU();
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,9 @@ public:
|
||||||
/// Shutdown the emulated system.
|
/// Shutdown the emulated system.
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
/// Forcibly detach the debugger if it is running.
|
||||||
|
void DetachDebugger();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> StallCPU();
|
std::unique_lock<std::mutex> StallCPU();
|
||||||
void UnstallCPU();
|
void UnstallCPU();
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||||
return received_data;
|
return received_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SignalType {
|
||||||
|
Stopped,
|
||||||
|
ShuttingDown,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SignalInfo {
|
||||||
|
SignalType type;
|
||||||
|
Kernel::KThread* thread;
|
||||||
|
};
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class DebuggerImpl : public DebuggerBackend {
|
class DebuggerImpl : public DebuggerBackend {
|
||||||
|
@ -56,7 +66,7 @@ public:
|
||||||
ShutdownServer();
|
ShutdownServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifyThreadStopped(Kernel::KThread* thread) {
|
bool SignalDebugger(SignalInfo signal_info) {
|
||||||
std::scoped_lock lk{connection_lock};
|
std::scoped_lock lk{connection_lock};
|
||||||
|
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
|
@ -64,9 +74,13 @@ public:
|
||||||
// It should be ignored.
|
// It should be ignored.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stopped = true;
|
|
||||||
|
|
||||||
boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
|
// Set up the state.
|
||||||
|
stopped = true;
|
||||||
|
info = signal_info;
|
||||||
|
|
||||||
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +138,7 @@ private:
|
||||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||||
|
|
||||||
// Set up the client signals for new data.
|
// 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); });
|
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||||
|
|
||||||
// Stop the emulated CPU.
|
// Stop the emulated CPU.
|
||||||
|
@ -142,9 +156,28 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipeData(std::span<const u8> data) {
|
void PipeData(std::span<const u8> data) {
|
||||||
AllCoreStop();
|
switch (info.type) {
|
||||||
UpdateActiveThread();
|
case SignalType::Stopped:
|
||||||
frontend->Stopped(active_thread);
|
// 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) {
|
void ClientData(std::span<const u8> data) {
|
||||||
|
@ -246,7 +279,9 @@ private:
|
||||||
boost::asio::ip::tcp::socket client_socket;
|
boost::asio::ip::tcp::socket client_socket;
|
||||||
std::optional<std::unique_lock<std::mutex>> suspend;
|
std::optional<std::unique_lock<std::mutex>> suspend;
|
||||||
|
|
||||||
|
SignalInfo info;
|
||||||
Kernel::KThread* active_thread;
|
Kernel::KThread* active_thread;
|
||||||
|
bool pipe_data;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
|
|
||||||
std::array<u8, 4096> client_data;
|
std::array<u8, 4096> client_data;
|
||||||
|
@ -263,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) {
|
||||||
Debugger::~Debugger() = default;
|
Debugger::~Debugger() = default;
|
||||||
|
|
||||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
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
|
} // namespace Core
|
||||||
|
|
|
@ -35,6 +35,11 @@ public:
|
||||||
*/
|
*/
|
||||||
bool NotifyThreadStopped(Kernel::KThread* thread);
|
bool NotifyThreadStopped(Kernel::KThread* thread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the debugger that a shutdown is being performed now and disconnect.
|
||||||
|
*/
|
||||||
|
void NotifyShutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<DebuggerImpl> impl;
|
std::unique_ptr<DebuggerImpl> impl;
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,6 +66,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void Stopped(Kernel::KThread* thread) = 0;
|
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.
|
* Called when new data is asynchronously received on the client socket.
|
||||||
* A list of actions to perform is returned.
|
* A list of actions to perform is returned.
|
||||||
|
|
|
@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default;
|
||||||
|
|
||||||
void GDBStub::Connected() {}
|
void GDBStub::Connected() {}
|
||||||
|
|
||||||
|
void GDBStub::ShuttingDown() {}
|
||||||
|
|
||||||
void GDBStub::Stopped(Kernel::KThread* thread) {
|
void GDBStub::Stopped(Kernel::KThread* thread) {
|
||||||
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ public:
|
||||||
|
|
||||||
void Connected() override;
|
void Connected() override;
|
||||||
void Stopped(Kernel::KThread* thread) override;
|
void Stopped(Kernel::KThread* thread) override;
|
||||||
|
void ShuttingDown() override;
|
||||||
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1545,6 +1545,7 @@ void GMainWindow::ShutdownGame() {
|
||||||
|
|
||||||
AllowOSSleep();
|
AllowOSSleep();
|
||||||
|
|
||||||
|
system->DetachDebugger();
|
||||||
discord_rpc->Pause();
|
discord_rpc->Pause();
|
||||||
emu_thread->RequestStop();
|
emu_thread->RequestStop();
|
||||||
|
|
||||||
|
|
|
@ -229,6 +229,7 @@ int main(int argc, char** argv) {
|
||||||
while (emu_window->IsOpen()) {
|
while (emu_window->IsOpen()) {
|
||||||
emu_window->WaitEvent();
|
emu_window->WaitEvent();
|
||||||
}
|
}
|
||||||
|
system.DetachDebugger();
|
||||||
void(system.Pause());
|
void(system.Pause());
|
||||||
system.Shutdown();
|
system.Shutdown();
|
||||||
|
|
||||||
|
|
Reference in New Issue