Keep threads from trying to suspend themselves on Linux
Avoids certain sporadic lockups (usually on startup)
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||||
#include "xenia/base/atomic.h"
|
#include "xenia/base/atomic.h"
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.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) {
|
if (thread->type() == XObject::Type::Thread) {
|
||||||
auto current_pcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
|
auto current_pcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
|
||||||
|
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
if (current_pcr->prcb_data.current_thread == thread->guest_object() ||
|
if (current_pcr->prcb_data.current_thread == thread->guest_object() ||
|
||||||
!thread->guest_object<X_KTHREAD>()->terminated) {
|
!thread->guest_object<X_KTHREAD>()->terminated) {
|
||||||
result = thread->Suspend(&suspend_count);
|
result = thread->Suspend(&suspend_count);
|
||||||
} else {
|
} else {
|
||||||
return X_STATUS_THREAD_IS_TERMINATING;
|
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<X_KTHREAD>()->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<X_KTHREAD>();
|
||||||
|
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 {
|
} else {
|
||||||
return X_STATUS_OBJECT_TYPE_MISMATCH;
|
return X_STATUS_OBJECT_TYPE_MISMATCH;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "xenia/base/byte_stream.h"
|
#include "xenia/base/byte_stream.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
@@ -717,12 +718,27 @@ X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
|
|||||||
if (out_suspend_count) {
|
if (out_suspend_count) {
|
||||||
*out_suspend_count = previous_suspend_count;
|
*out_suspend_count = previous_suspend_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t unused_host_suspend_count = 0;
|
uint32_t unused_host_suspend_count = 0;
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
if (thread_->Resume(&unused_host_suspend_count)) {
|
if (thread_->Resume(&unused_host_suspend_count)) {
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
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) {
|
X_STATUS XThread::Suspend(uint32_t* out_suspend_count) {
|
||||||
|
|||||||
Reference in New Issue
Block a user