diff --git a/src/xenia/debug/debug_server.h b/src/xenia/debug/debug_server.h index 07da439eb..ac61d7337 100644 --- a/src/xenia/debug/debug_server.h +++ b/src/xenia/debug/debug_server.h @@ -10,6 +10,8 @@ #ifndef XENIA_DEBUG_DEBUG_SERVER_H_ #define XENIA_DEBUG_DEBUG_SERVER_H_ +#include + #include "xenia/cpu/function.h" #include "xenia/cpu/processor.h" #include "xenia/debug/breakpoint.h" @@ -27,6 +29,8 @@ class DebugServer { virtual bool Initialize() = 0; + virtual void PostSynchronous(std::function fn) = 0; + // TODO(benvanik): better thread type (XThread?) // virtual void OnThreadCreated(ThreadState* thread_state) = 0; // virtual void OnThreadDestroyed(ThreadState* thread_state) = 0; diff --git a/src/xenia/debug/debugger.cc b/src/xenia/debug/debugger.cc index 9d7c17f1b..8fda12747 100644 --- a/src/xenia/debug/debugger.cc +++ b/src/xenia/debug/debugger.cc @@ -123,7 +123,7 @@ void Debugger::PreLaunch() { // Start paused. execution_state_ = ExecutionState::kRunning; - Interrupt(); + server_->PostSynchronous([this]() { Interrupt(); }); } else { // Start running. execution_state_ = ExecutionState::kRunning; diff --git a/src/xenia/debug/server/gdb/gdb_server.cc b/src/xenia/debug/server/gdb/gdb_server.cc index d17448242..08d2e25c1 100644 --- a/src/xenia/debug/server/gdb/gdb_server.cc +++ b/src/xenia/debug/server/gdb/gdb_server.cc @@ -44,6 +44,8 @@ bool GdbServer::Initialize() { return true; } +void GdbServer::PostSynchronous(std::function fn) { assert_always(); } + void GdbServer::AcceptClient(std::unique_ptr client) { // If we have an existing client, kill it and join its thread. if (client_) { diff --git a/src/xenia/debug/server/gdb/gdb_server.h b/src/xenia/debug/server/gdb/gdb_server.h index 2e6b3019e..645306dcb 100644 --- a/src/xenia/debug/server/gdb/gdb_server.h +++ b/src/xenia/debug/server/gdb/gdb_server.h @@ -32,6 +32,8 @@ class GdbServer : public DebugServer { bool Initialize() override; + void PostSynchronous(std::function fn) override; + private: void AcceptClient(std::unique_ptr client); bool HandleClientEvent(); diff --git a/src/xenia/debug/server/mi/mi_server.cc b/src/xenia/debug/server/mi/mi_server.cc index 19e0f5fd6..a0e3a52e7 100644 --- a/src/xenia/debug/server/mi/mi_server.cc +++ b/src/xenia/debug/server/mi/mi_server.cc @@ -44,6 +44,8 @@ bool MIServer::Initialize() { return true; } +void MIServer::PostSynchronous(std::function fn) { assert_always(); } + void MIServer::AcceptClient(std::unique_ptr client) { // If we have an existing client, kill it and join its thread. if (client_) { diff --git a/src/xenia/debug/server/mi/mi_server.h b/src/xenia/debug/server/mi/mi_server.h index 9d109d1eb..8cd5c9a73 100644 --- a/src/xenia/debug/server/mi/mi_server.h +++ b/src/xenia/debug/server/mi/mi_server.h @@ -32,6 +32,8 @@ class MIServer : public DebugServer { bool Initialize() override; + void PostSynchronous(std::function fn) override; + private: void AcceptClient(std::unique_ptr client); bool HandleClientEvent(); diff --git a/src/xenia/debug/server/xdp/xdp_server.cc b/src/xenia/debug/server/xdp/xdp_server.cc index 2b6bc386a..3a2985af7 100644 --- a/src/xenia/debug/server/xdp/xdp_server.cc +++ b/src/xenia/debug/server/xdp/xdp_server.cc @@ -42,6 +42,8 @@ XdpServer::XdpServer(Debugger* debugger) XdpServer::~XdpServer() = default; bool XdpServer::Initialize() { + post_event_ = xe::threading::Event::CreateAutoResetEvent(false); + socket_server_ = SocketServer::Create(uint16_t(FLAGS_xdp_server_port), [this](std::unique_ptr client) { AcceptClient(std::move(client)); @@ -54,6 +56,19 @@ bool XdpServer::Initialize() { return true; } +void XdpServer::PostSynchronous(std::function fn) { + xe::threading::Fence fence; + { + std::lock_guard lock(post_mutex_); + post_queue_.push_back([&fence, fn]() { + fn(); + fence.Signal(); + }); + } + post_event_->Set(); + fence.Wait(); +} + void XdpServer::AcceptClient(std::unique_ptr client) { // If we have an existing client, kill it and join its thread. if (client_) { @@ -85,11 +100,35 @@ void XdpServer::AcceptClient(std::unique_ptr client) { // Main loop. bool running = true; while (running) { - auto wait_result = xe::threading::Wait(client_->wait_handle(), true); - switch (wait_result) { + xe::threading::WaitHandle* wait_handles[] = { + client_->wait_handle(), post_event_.get(), + }; + auto wait_result = xe::threading::WaitMultiple( + wait_handles, xe::countof(wait_handles), false, true); + switch (wait_result.first) { case xe::threading::WaitResult::kSuccess: // Event (read or close). - running = HandleClientEvent(); + switch (wait_result.second) { + case 0: { + running = HandleClientEvent(); + } break; + case 1: { + bool has_remaining = true; + while (has_remaining) { + std::function fn; + { + std::lock_guard lock(post_mutex_); + fn = std::move(post_queue_.front()); + post_queue_.pop_front(); + has_remaining = !post_queue_.empty(); + if (!has_remaining) { + post_event_->Reset(); + } + } + fn(); + } + } break; + } continue; case xe::threading::WaitResult::kAbandoned: case xe::threading::WaitResult::kFailed: diff --git a/src/xenia/debug/server/xdp/xdp_server.h b/src/xenia/debug/server/xdp/xdp_server.h index c5f5db4da..6dd6cd510 100644 --- a/src/xenia/debug/server/xdp/xdp_server.h +++ b/src/xenia/debug/server/xdp/xdp_server.h @@ -10,7 +10,9 @@ #ifndef XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_ #define XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_ +#include #include +#include #include "xenia/base/socket.h" #include "xenia/base/threading.h" @@ -33,6 +35,8 @@ class XdpServer : public DebugServer { bool Initialize() override; + void PostSynchronous(std::function fn) override; + void OnExecutionContinued() override; void OnExecutionInterrupted() override; @@ -54,6 +58,11 @@ class XdpServer : public DebugServer { std::unique_ptr client_; std::unique_ptr client_thread_; + + std::mutex post_mutex_; + std::unique_ptr post_event_; + std::list> post_queue_; + std::vector receive_buffer_; proto::PacketReader packet_reader_; proto::PacketWriter packet_writer_; diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index f0b4a6d04..8bef4c77e 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -218,8 +218,10 @@ X_STATUS XUserModule::Launch(uint32_t flags) { XELOGI("Launching module..."); // Create a thread to run in. - auto thread = object_ref( - new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0, true)); + // We start suspended so we can run the debugger prep. + auto thread = object_ref(new XThread(kernel_state(), stack_size_, 0, + entry_point_, 0, + X_CREATE_SUSPENDED, true)); X_STATUS result = thread->Create(); if (XFAILED(result)) { @@ -227,6 +229,16 @@ X_STATUS XUserModule::Launch(uint32_t flags) { return result; } + // Waits for a debugger client, if desired. + if (emulator()->debugger()) { + emulator()->debugger()->PreLaunch(); + } + + // Resume the thread now. + // If the debugger has requested a suspend this will just decrement the + // suspend count without resuming it until the debugger wants. + thread->Resume(); + // Wait until thread completes. thread->Wait(0, 0, 0, nullptr); diff --git a/src/xenia/kernel/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl_module.cc index 6a5579d1b..399feaf46 100644 --- a/src/xenia/kernel/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl_module.cc @@ -179,11 +179,6 @@ int XboxkrnlModule::LaunchModule(const char* path) { // Set as the main module, while running. kernel_state_->SetExecutableModule(module); - // Waits for a debugger client, if desired. - if (emulator()->debugger()) { - emulator()->debugger()->PreLaunch(); - } - // Launch the module. // NOTE: this won't return until the module exits. X_STATUS result_code = module->Launch(0);