[XThread] Replace guest exception handling with setjmp/longjmp
This commit is contained in:
@@ -513,16 +513,6 @@ X_STATUS XThread::Terminate(int exit_code) {
|
|||||||
return X_STATUS_SUCCESS;
|
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() {
|
void XThread::Execute() {
|
||||||
XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})",
|
XELOGKERNEL("XThread::Execute thid {} (handle={:08X}, '{}', native={:08X})",
|
||||||
thread_id_, handle(), thread_name_, thread_->system_id());
|
thread_id_, handle(), thread_name_, thread_->system_id());
|
||||||
@@ -551,25 +541,34 @@ void XThread::Execute() {
|
|||||||
want_exit_code = true;
|
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;
|
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<int>(kernel_state()->processor()->Execute(
|
exit_code = static_cast<int>(kernel_state()->processor()->Execute(
|
||||||
thread_state_, address, args.data(), args.size()));
|
thread_state_, address, args.data(), args.size()));
|
||||||
next_address = 0;
|
next_address = 0;
|
||||||
} catch (const reenter_exception& ree) {
|
|
||||||
next_address = ree.address();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle reentry loop for fiber switching.
|
||||||
// See XThread::Reenter comments.
|
// See XThread::Reenter comments.
|
||||||
while (next_address != 0) {
|
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);
|
kernel_state()->processor()->ExecuteRaw(thread_state_, next_address);
|
||||||
next_address = 0;
|
next_address = 0;
|
||||||
if (want_exit_code) {
|
if (want_exit_code) {
|
||||||
exit_code = static_cast<int>(thread_state_->context()->r[3]);
|
exit_code = static_cast<int>(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) {
|
void XThread::Reenter(uint32_t address) {
|
||||||
// TODO(gibbed): Maybe use setjmp/longjmp on Windows?
|
// Use setjmp/longjmp instead of exceptions to avoid issues with unwinding
|
||||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/longjmp#remarks
|
// through JIT-compiled code, which lacks exception handling metadata.
|
||||||
// On Windows with /EH, setjmp/longjmp do stack unwinding.
|
// This is called when the game switches fiber stacks (e.g., via
|
||||||
// Is there a better solution than exceptions for stack unwinding?
|
// KeSetCurrentStackPointers in games like Forza Horizon 2).
|
||||||
throw reenter_exception(address);
|
reentry_address_ = address;
|
||||||
|
std::longjmp(reentry_jmp_buf_, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XThread::EnterCriticalRegion() {
|
void XThread::EnterCriticalRegion() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#define XENIA_KERNEL_XTHREAD_H_
|
#define XENIA_KERNEL_XTHREAD_H_
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <csetjmp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/base/mutex.h"
|
#include "xenia/base/mutex.h"
|
||||||
@@ -466,6 +467,10 @@ class XThread : public XObject, public cpu::Thread {
|
|||||||
|
|
||||||
int32_t priority_ = 0;
|
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_;
|
std::mutex thread_lock_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user