From 22f21fada9f42da6f107b6b1d33a292dccf077b7 Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:44:09 +0900 Subject: [PATCH] [XThread] Replace guest exception handling with setjmp/longjmp --- src/xenia/kernel/xthread.cc | 42 ++++++++++++++++++------------------- src/xenia/kernel/xthread.h | 5 +++++ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 4948eca55..fdbbd308c 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -513,16 +513,6 @@ X_STATUS XThread::Terminate(int exit_code) { return X_STATUS_SUCCESS; } -class reenter_exception { - public: - reenter_exception(uint32_t address) : address_(address) {}; - virtual ~reenter_exception() {}; - uint32_t address() const { return address_; } - - private: - uint32_t address_; -}; - void XThread::Execute() { XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})", thread_id_, handle(), thread_name_, thread_->system_id()); @@ -551,25 +541,34 @@ void XThread::Execute() { want_exit_code = true; } + // Set up reentry jump buffer for fiber-based stack switching. + // When Reenter() is called (e.g., by KeSetCurrentStackPointers), it will + // longjmp back here instead of throwing an exception through JIT code. uint32_t next_address; - try { + if (setjmp(reentry_jmp_buf_) != 0) { + // Longjmp returned here - reentry requested + next_address = reentry_address_; + } else { + // Initial execution exit_code = static_cast(kernel_state()->processor()->Execute( thread_state_, address, args.data(), args.size())); next_address = 0; - } catch (const reenter_exception& ree) { - next_address = ree.address(); } + // Handle reentry loop for fiber switching. // See XThread::Reenter comments. while (next_address != 0) { - try { + // Set up jump buffer for potential reentries during this execution + if (setjmp(reentry_jmp_buf_) != 0) { + // Nested reentry occurred + next_address = reentry_address_; + } else { + // Execute at the reentry address kernel_state()->processor()->ExecuteRaw(thread_state_, next_address); next_address = 0; if (want_exit_code) { exit_code = static_cast(thread_state_->context()->r[3]); } - } catch (const reenter_exception& ree) { - next_address = ree.address(); } } @@ -579,11 +578,12 @@ void XThread::Execute() { } void XThread::Reenter(uint32_t address) { - // TODO(gibbed): Maybe use setjmp/longjmp on Windows? - // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/longjmp#remarks - // On Windows with /EH, setjmp/longjmp do stack unwinding. - // Is there a better solution than exceptions for stack unwinding? - throw reenter_exception(address); + // Use setjmp/longjmp instead of exceptions to avoid issues with unwinding + // through JIT-compiled code, which lacks exception handling metadata. + // This is called when the game switches fiber stacks (e.g., via + // KeSetCurrentStackPointers in games like Forza Horizon 2). + reentry_address_ = address; + std::longjmp(reentry_jmp_buf_, 1); } void XThread::EnterCriticalRegion() { diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index d4ec9e848..5e42a570e 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -11,6 +11,7 @@ #define XENIA_KERNEL_XTHREAD_H_ #include +#include #include #include "xenia/base/mutex.h" @@ -466,6 +467,10 @@ class XThread : public XObject, public cpu::Thread { int32_t priority_ = 0; + // Reentry context for setjmp/longjmp based stack unwinding + std::jmp_buf reentry_jmp_buf_; + uint32_t reentry_address_ = 0; + std::mutex thread_lock_; };