diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index e8740e201..e6fa86ba1 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -10,6 +10,7 @@ #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/base/atomic.h" #include "xenia/base/clock.h" +#include "xenia/base/platform.h" #include "xenia/cpu/processor.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" @@ -236,12 +237,39 @@ dword_result_t NtSuspendThread_entry(dword_t handle, if (thread->type() == XObject::Type::Thread) { auto current_pcr = context->TranslateVirtualGPR(context->r[13]); +#if XE_PLATFORM_WIN32 if (current_pcr->prcb_data.current_thread == thread->guest_object() || !thread->guest_object()->terminated) { result = thread->Suspend(&suspend_count); } else { return X_STATUS_THREAD_IS_TERMINATING; } +#elif XE_PLATFORM_LINUX + // On Linux, we need to handle self-suspension specially to avoid deadlock + if (!thread->guest_object()->terminated) { + bool is_self_suspend = + (current_pcr->prcb_data.current_thread == thread->guest_object()); + + if (is_self_suspend) { + // Self-suspension: just increment the suspend count and return + // The thread continues running - this matches Windows/Xbox behavior + auto guest_thread = thread->guest_object(); + suspend_count = guest_thread->suspend_count; + guest_thread->suspend_count++; + result = X_STATUS_SUCCESS; + XELOGD( + "Thread {:X} self-suspending (count: {}) - continuing execution", + thread->handle(), guest_thread->suspend_count); + } else { + // Normal suspension of another thread + result = thread->Suspend(&suspend_count); + } + } else { + return X_STATUS_THREAD_IS_TERMINATING; + } +#else +#error "Unsupported platform" +#endif } else { return X_STATUS_OBJECT_TYPE_MISMATCH; } diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 88523c80a..4948eca55 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -11,6 +11,7 @@ #include "xenia/base/byte_stream.h" #include "xenia/base/logging.h" +#include "xenia/base/platform.h" #include "xenia/base/profiling.h" #include "xenia/base/threading.h" #include "xenia/cpu/processor.h" @@ -717,12 +718,27 @@ X_STATUS XThread::Resume(uint32_t* out_suspend_count) { if (out_suspend_count) { *out_suspend_count = previous_suspend_count; } + uint32_t unused_host_suspend_count = 0; +#if XE_PLATFORM_WIN32 if (thread_->Resume(&unused_host_suspend_count)) { return X_STATUS_SUCCESS; } else { return X_STATUS_UNSUCCESSFUL; } +#elif XE_PLATFORM_LINUX + // On Linux, only resume if suspend count is 0 + // Resume might fail if thread was self-suspended - this is expected + if (guest_thread->suspend_count == 0) { + if (!thread_->Resume(&unused_host_suspend_count)) { + XELOGD("Host thread resume skipped for thread {:X} (was self-suspended)", + handle()); + } + } + return X_STATUS_SUCCESS; +#else +#error "Unsupported platform" +#endif } X_STATUS XThread::Suspend(uint32_t* out_suspend_count) {