mirror of
https://github.com/FEX-Emu/FEX.git
synced 2024-11-23 14:40:14 +00:00
FEXLoader: Changes frontend thread management to wrap FEXCore thread objects
A bit of refactoring necessary before we can move the remaining Linux specific code to the frontend. Most of this taken from #3535 but attempting to be NFC as much as possible.
This commit is contained in:
parent
729e32ccc2
commit
d372552593
@ -544,7 +544,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
FEX::AOT::AOTGenSection(CTX.get(), Section);
|
||||
}
|
||||
} else {
|
||||
CTX->RunUntilExit(ParentThread);
|
||||
CTX->RunUntilExit(ParentThread->Thread);
|
||||
}
|
||||
|
||||
if (AOTEnabled) {
|
||||
@ -565,10 +565,10 @@ int main(int argc, char** argv, char** const envp) {
|
||||
}
|
||||
}
|
||||
|
||||
auto ProgramStatus = ParentThread->StatusCode;
|
||||
auto ProgramStatus = ParentThread->Thread->StatusCode;
|
||||
|
||||
SignalDelegation->UninstallTLSState(ParentThread);
|
||||
CTX->DestroyThread(ParentThread);
|
||||
SyscallHandler->TM.DestroyThread(ParentThread);
|
||||
|
||||
DebugServer.reset();
|
||||
SyscallHandler.reset();
|
||||
|
@ -320,14 +320,14 @@ fextl::string GdbServer::readRegs() {
|
||||
FEXCore::Core::CPUState state {};
|
||||
|
||||
auto Threads = SyscallHandler->TM.GetThreads();
|
||||
FEXCore::Core::InternalThreadState* CurrentThread {Threads->at(0)};
|
||||
FEX::HLE::ThreadStateObject* CurrentThread {Threads->at(0)};
|
||||
bool Found = false;
|
||||
|
||||
for (auto& Thread : *Threads) {
|
||||
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
if (Thread->Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
continue;
|
||||
}
|
||||
memcpy(&state, Thread->CurrentFrame, sizeof(state));
|
||||
memcpy(&state, Thread->Thread->CurrentFrame, sizeof(state));
|
||||
CurrentThread = Thread;
|
||||
Found = true;
|
||||
break;
|
||||
@ -335,14 +335,14 @@ fextl::string GdbServer::readRegs() {
|
||||
|
||||
if (!Found) {
|
||||
// If set to an invalid thread then just get the parent thread ID
|
||||
memcpy(&state, CurrentThread->CurrentFrame, sizeof(state));
|
||||
memcpy(&state, CurrentThread->Thread->CurrentFrame, sizeof(state));
|
||||
}
|
||||
|
||||
// Encode the GDB context definition
|
||||
memcpy(&GDB.gregs[0], &state.gregs[0], sizeof(GDB.gregs));
|
||||
memcpy(&GDB.rip, &state.rip, sizeof(GDB.rip));
|
||||
|
||||
GDB.eflags = CTX->ReconstructCompactedEFLAGS(CurrentThread, false, nullptr, 0);
|
||||
GDB.eflags = CTX->ReconstructCompactedEFLAGS(CurrentThread->Thread, false, nullptr, 0);
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
||||
memcpy(&GDB.mm[i], &state.mm[i], sizeof(GDB.mm));
|
||||
@ -371,14 +371,14 @@ GdbServer::HandledPacketType GdbServer::readReg(const fextl::string& packet) {
|
||||
FEXCore::Core::CPUState state {};
|
||||
|
||||
auto Threads = SyscallHandler->TM.GetThreads();
|
||||
FEXCore::Core::InternalThreadState* CurrentThread {Threads->at(0)};
|
||||
FEX::HLE::ThreadStateObject* CurrentThread {Threads->at(0)};
|
||||
bool Found = false;
|
||||
|
||||
for (auto& Thread : *Threads) {
|
||||
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
if (Thread->Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
continue;
|
||||
}
|
||||
memcpy(&state, Thread->CurrentFrame, sizeof(state));
|
||||
memcpy(&state, Thread->Thread->CurrentFrame, sizeof(state));
|
||||
CurrentThread = Thread;
|
||||
Found = true;
|
||||
break;
|
||||
@ -386,7 +386,7 @@ GdbServer::HandledPacketType GdbServer::readReg(const fextl::string& packet) {
|
||||
|
||||
if (!Found) {
|
||||
// If set to an invalid thread then just get the parent thread ID
|
||||
memcpy(&state, CurrentThread->CurrentFrame, sizeof(state));
|
||||
memcpy(&state, CurrentThread->Thread->CurrentFrame, sizeof(state));
|
||||
}
|
||||
|
||||
|
||||
@ -395,7 +395,7 @@ GdbServer::HandledPacketType GdbServer::readReg(const fextl::string& packet) {
|
||||
} else if (addr == offsetof(GDBContextDefinition, rip)) {
|
||||
return {encodeHex((unsigned char*)(&state.rip), sizeof(uint64_t)), HandledPacketType::TYPE_ACK};
|
||||
} else if (addr == offsetof(GDBContextDefinition, eflags)) {
|
||||
uint32_t eflags = CTX->ReconstructCompactedEFLAGS(CurrentThread, false, nullptr, 0);
|
||||
uint32_t eflags = CTX->ReconstructCompactedEFLAGS(CurrentThread->Thread, false, nullptr, 0);
|
||||
|
||||
return {encodeHex((unsigned char*)(&eflags), sizeof(uint32_t)), HandledPacketType::TYPE_ACK};
|
||||
} else if (addr >= offsetof(GDBContextDefinition, cs) && addr < offsetof(GDBContextDefinition, mm[0])) {
|
||||
@ -722,8 +722,8 @@ GdbServer::HandledPacketType GdbServer::handleXfer(const fextl::string& packet)
|
||||
ss << "<threads>\n";
|
||||
for (auto& Thread : *Threads) {
|
||||
// Thread id is in hex without 0x prefix
|
||||
const auto ThreadName = getThreadName(Thread->ThreadManager.GetTID());
|
||||
ss << "<thread id=\"" << std::hex << Thread->ThreadManager.GetTID() << "\"";
|
||||
const auto ThreadName = getThreadName(Thread->Thread->ThreadManager.GetTID());
|
||||
ss << "<thread id=\"" << std::hex << Thread->Thread->ThreadManager.GetTID() << "\"";
|
||||
if (!ThreadName.empty()) {
|
||||
ss << " name=\"" << ThreadName << "\"";
|
||||
}
|
||||
@ -962,7 +962,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(const fextl::string& packet)
|
||||
ss << "m";
|
||||
for (size_t i = 0; i < Threads->size(); ++i) {
|
||||
auto Thread = Threads->at(i);
|
||||
ss << std::hex << Thread->ThreadManager.TID;
|
||||
ss << std::hex << Thread->Thread->ThreadManager.TID;
|
||||
if (i != (Threads->size() - 1)) {
|
||||
ss << ",";
|
||||
}
|
||||
@ -985,7 +985,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(const fextl::string& packet)
|
||||
// Returns the current Thread ID
|
||||
auto Threads = SyscallHandler->TM.GetThreads();
|
||||
fextl::ostringstream ss;
|
||||
ss << "m" << std::hex << Threads->at(0)->ThreadManager.TID;
|
||||
ss << "m" << std::hex << Threads->at(0)->Thread->ThreadManager.TID;
|
||||
return {ss.str(), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
if (match("QStartNoAckMode")) {
|
||||
|
@ -54,7 +54,7 @@ constexpr static uint32_t X86_MINSIGSTKSZ = 0x2000U;
|
||||
static SignalDelegator* GlobalDelegator {};
|
||||
|
||||
struct ThreadState {
|
||||
FEXCore::Core::InternalThreadState* Thread {};
|
||||
FEX::HLE::ThreadStateObject* Thread {};
|
||||
|
||||
void* AltStackPtr {};
|
||||
stack_t GuestAltStack {
|
||||
@ -148,19 +148,19 @@ void SignalDelegator::HandleSignal(int Signal, void* Info, void* UContext) {
|
||||
} else {
|
||||
SignalHandler& Handler = HostHandlers[Signal];
|
||||
for (auto& HandlerFunc : Handler.Handlers) {
|
||||
if (HandlerFunc(Thread, Signal, Info, UContext)) {
|
||||
if (HandlerFunc(Thread->Thread, Signal, Info, UContext)) {
|
||||
// If the host handler handled the fault then we can continue now
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Handler.FrontendHandler && Handler.FrontendHandler(Thread, Signal, Info, UContext)) {
|
||||
if (Handler.FrontendHandler && Handler.FrontendHandler(Thread->Thread, Signal, Info, UContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now let the frontend handle the signal
|
||||
// It's clearly a guest signal and this ends up being an OS specific issue
|
||||
HandleGuestSignal(Thread, Signal, Info, UContext);
|
||||
HandleGuestSignal(Thread->Thread, Signal, Info, UContext);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1739,11 +1739,11 @@ SignalDelegator::~SignalDelegator() {
|
||||
GlobalDelegator = nullptr;
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* SignalDelegator::GetTLSThread() {
|
||||
FEX::HLE::ThreadStateObject* SignalDelegator::GetTLSThread() {
|
||||
return ThreadData.Thread;
|
||||
}
|
||||
|
||||
void SignalDelegator::RegisterTLSState(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void SignalDelegator::RegisterTLSState(FEX::HLE::ThreadStateObject* Thread) {
|
||||
ThreadData.Thread = Thread;
|
||||
|
||||
// Set up our signal alternative stack
|
||||
@ -1764,14 +1764,14 @@ void SignalDelegator::RegisterTLSState(FEXCore::Core::InternalThreadState* Threa
|
||||
// Get the current host signal mask
|
||||
::syscall(SYS_rt_sigprocmask, 0, nullptr, &ThreadData.CurrentSignalMask.Val, 8);
|
||||
|
||||
if (Thread != (FEXCore::Core::InternalThreadState*)UINTPTR_MAX) {
|
||||
if (Thread->Thread) {
|
||||
// Reserve a small amount of deferred signal frames. Usually the stack won't be utilized beyond
|
||||
// 1 or 2 signals but add a few more just in case.
|
||||
Thread->DeferredSignalFrames.reserve(8);
|
||||
Thread->Thread->DeferredSignalFrames.reserve(8);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalDelegator::UninstallTLSState(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void SignalDelegator::UninstallTLSState(FEX::HLE::ThreadStateObject* Thread) {
|
||||
FEXCore::Allocator::munmap(ThreadData.AltStackPtr, SIGSTKSZ * 16);
|
||||
|
||||
ThreadData.AltStackPtr = nullptr;
|
||||
@ -1874,7 +1874,7 @@ uint64_t SignalDelegator::RegisterGuestSigAltStack(const stack_t* ss, stack_t* o
|
||||
bool UsingAltStack {};
|
||||
uint64_t AltStackBase = reinterpret_cast<uint64_t>(ThreadData.GuestAltStack.ss_sp);
|
||||
uint64_t AltStackEnd = AltStackBase + ThreadData.GuestAltStack.ss_size;
|
||||
uint64_t GuestSP = Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP];
|
||||
uint64_t GuestSP = Thread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP];
|
||||
|
||||
if (!(ThreadData.GuestAltStack.ss_flags & SS_DISABLE) && GuestSP >= AltStackBase && GuestSP <= AltStackEnd) {
|
||||
UsingAltStack = true;
|
||||
@ -1977,7 +1977,7 @@ uint64_t SignalDelegator::GuestSigProcMask(int how, const uint64_t* set, uint64_
|
||||
*oldset = OldSet;
|
||||
}
|
||||
|
||||
CheckForPendingSignals(GetTLSThread());
|
||||
CheckForPendingSignals(GetTLSThread()->Thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2040,7 +2040,7 @@ uint64_t SignalDelegator::GuestSigSuspend(uint64_t* set, size_t sigsetsize) {
|
||||
// then this is safe-ish
|
||||
ThreadData.CurrentSignalMask = ThreadData.PreviousSuspendMask;
|
||||
|
||||
CheckForPendingSignals(GetTLSThread());
|
||||
CheckForPendingSignals(GetTLSThread()->Thread);
|
||||
|
||||
return Result == -1 ? -errno : Result;
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ public:
|
||||
// Called from the signal trampoline function.
|
||||
void HandleSignal(int Signal, void* Info, void* UContext);
|
||||
|
||||
void RegisterTLSState(FEXCore::Core::InternalThreadState* Thread);
|
||||
void UninstallTLSState(FEXCore::Core::InternalThreadState* Thread);
|
||||
void RegisterTLSState(FEX::HLE::ThreadStateObject* Thread);
|
||||
void UninstallTLSState(FEX::HLE::ThreadStateObject* Thread);
|
||||
|
||||
/**
|
||||
* @brief Registers a signal handler for the host to handle a signal
|
||||
@ -134,7 +134,7 @@ public:
|
||||
|
||||
void SaveTelemetry();
|
||||
private:
|
||||
FEXCore::Core::InternalThreadState* GetTLSThread();
|
||||
FEX::HLE::ThreadStateObject* GetTLSThread();
|
||||
|
||||
// Called from the thunk handler to handle the signal
|
||||
void HandleGuestSignal(FEXCore::Core::InternalThreadState* Thread, int Signal, void* Info, void* UContext);
|
||||
|
@ -363,7 +363,7 @@ static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) {
|
||||
}
|
||||
|
||||
struct StackFrameData {
|
||||
FEXCore::Core::InternalThreadState* Thread {};
|
||||
FEX::HLE::ThreadStateObject* Thread {};
|
||||
FEXCore::Context::Context* CTX {};
|
||||
FEXCore::Core::CpuStateFrame NewFrame {};
|
||||
FEX::HLE::clone3_args GuestArgs {};
|
||||
@ -443,7 +443,7 @@ static void PrintFlags(uint64_t Flags) {
|
||||
|
||||
static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args* args) {
|
||||
StackFrameData* Data = (StackFrameData*)FEXCore::Allocator::malloc(sizeof(StackFrameData));
|
||||
Data->Thread = Frame->Thread;
|
||||
Data->Thread = static_cast<FEX::HLE::ThreadStateObject*>(Frame->Thread->FrontendPtr);
|
||||
Data->CTX = Frame->Thread->CTX;
|
||||
Data->GuestArgs = *args;
|
||||
|
||||
@ -469,7 +469,7 @@ static uint64_t Clone3Handler(FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clo
|
||||
constexpr size_t Offset = sizeof(StackFramePlusRet);
|
||||
StackFramePlusRet* Data = (StackFramePlusRet*)(reinterpret_cast<uint64_t>(args->NewStack) + args->StackSize - Offset);
|
||||
Data->Ret = (uint64_t)Clone3HandlerRet;
|
||||
Data->Data.Thread = Frame->Thread;
|
||||
Data->Data.Thread = static_cast<FEX::HLE::ThreadStateObject*>(Frame->Thread->FrontendPtr);
|
||||
Data->Data.CTX = Frame->Thread->CTX;
|
||||
Data->Data.GuestArgs = *args;
|
||||
|
||||
@ -609,17 +609,17 @@ uint64_t CloneHandler(FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args
|
||||
auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, args);
|
||||
|
||||
// Return the new threads TID
|
||||
uint64_t Result = NewThread->ThreadManager.GetTID();
|
||||
uint64_t Result = NewThread->Thread->ThreadManager.GetTID();
|
||||
|
||||
// Actually start the thread
|
||||
FEX::HLE::_SyscallHandler->TM.RunThread(NewThread);
|
||||
|
||||
if (flags & CLONE_VFORK) {
|
||||
// If VFORK is set then the calling process is suspended until the thread exits with execve or exit
|
||||
NewThread->ExecutionThread->join(nullptr);
|
||||
NewThread->Thread->ExecutionThread->join(nullptr);
|
||||
|
||||
// Normally a thread cleans itself up on exit. But because we need to join, we are now responsible
|
||||
Thread->CTX->DestroyThread(NewThread);
|
||||
FEX::HLE::_SyscallHandler->TM.DestroyThread(NewThread);
|
||||
}
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
|
@ -44,7 +44,7 @@ namespace FEX::HLE {
|
||||
|
||||
struct ExecutionThreadHandler {
|
||||
FEXCore::Context::Context* CTX;
|
||||
FEXCore::Core::InternalThreadState* Thread;
|
||||
FEX::HLE::ThreadStateObject* Thread;
|
||||
};
|
||||
|
||||
static void* ThreadHandler(void* Data) {
|
||||
@ -53,14 +53,13 @@ static void* ThreadHandler(void* Data) {
|
||||
auto Thread = Handler->Thread;
|
||||
FEXCore::Allocator::free(Handler);
|
||||
FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterTLSState(Thread);
|
||||
CTX->ExecutionThread(Thread);
|
||||
CTX->ExecutionThread(Thread->Thread);
|
||||
FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread);
|
||||
FEX::HLE::_SyscallHandler->TM.DestroyThread(Thread);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState*
|
||||
CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args* args) {
|
||||
FEX::HLE::ThreadStateObject* CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args* args) {
|
||||
uint64_t flags = args->args.flags;
|
||||
FEXCore::Core::CPUState NewThreadState {};
|
||||
// Clone copies the parent thread's state
|
||||
@ -79,28 +78,28 @@ CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Fr
|
||||
|
||||
if (FEX::HLE::_SyscallHandler->Is64BitMode()) {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x64::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->args.tls));
|
||||
x64::SetThreadArea(NewThread->Thread->CurrentFrame, reinterpret_cast<void*>(args->args.tls));
|
||||
}
|
||||
// Set us to start just after the syscall instruction
|
||||
x64::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
x64::AdjustRipForNewThread(NewThread->Thread->CurrentFrame);
|
||||
} else {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x32::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->args.tls));
|
||||
x32::SetThreadArea(NewThread->Thread->CurrentFrame, reinterpret_cast<void*>(args->args.tls));
|
||||
}
|
||||
x32::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
x32::AdjustRipForNewThread(NewThread->Thread->CurrentFrame);
|
||||
}
|
||||
|
||||
// We need to do some post-thread creation setup.
|
||||
NewThread->StartPaused = true;
|
||||
NewThread->Thread->StartPaused = true;
|
||||
|
||||
// Initialize a new thread for execution.
|
||||
ExecutionThreadHandler* Arg = reinterpret_cast<ExecutionThreadHandler*>(FEXCore::Allocator::malloc(sizeof(ExecutionThreadHandler)));
|
||||
Arg->CTX = CTX;
|
||||
Arg->Thread = NewThread;
|
||||
NewThread->ExecutionThread = FEXCore::Threads::Thread::Create(ThreadHandler, Arg);
|
||||
NewThread->Thread->ExecutionThread = FEXCore::Threads::Thread::Create(ThreadHandler, Arg);
|
||||
|
||||
// Wait for the thread to have started.
|
||||
NewThread->ThreadWaiting.Wait();
|
||||
NewThread->Thread->ThreadWaiting.Wait();
|
||||
|
||||
if (FEX::HLE::_SyscallHandler->NeedXIDCheck()) {
|
||||
// The first time an application creates a thread, GLIBC installs their SETXID signal handler.
|
||||
@ -112,7 +111,7 @@ CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Fr
|
||||
}
|
||||
|
||||
// Return the new threads TID
|
||||
uint64_t Result = NewThread->ThreadManager.GetTID();
|
||||
uint64_t Result = NewThread->Thread->ThreadManager.GetTID();
|
||||
|
||||
// Sets the child TID to pointer in ParentTID
|
||||
if (flags & CLONE_PARENT_SETTID) {
|
||||
@ -121,7 +120,7 @@ CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Fr
|
||||
|
||||
// Sets the child TID to the pointer in ChildTID
|
||||
if (flags & CLONE_CHILD_SETTID) {
|
||||
NewThread->ThreadManager.set_child_tid = reinterpret_cast<int32_t*>(args->args.child_tid);
|
||||
NewThread->Thread->ThreadManager.set_child_tid = reinterpret_cast<int32_t*>(args->args.child_tid);
|
||||
*reinterpret_cast<pid_t*>(args->args.child_tid) = Result;
|
||||
}
|
||||
|
||||
@ -129,7 +128,7 @@ CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Fr
|
||||
// Additionally wakeup a futex at that address
|
||||
// Address /may/ be changed with SET_TID_ADDRESS syscall
|
||||
if (flags & CLONE_CHILD_CLEARTID) {
|
||||
NewThread->ThreadManager.clear_child_tid = reinterpret_cast<int32_t*>(args->args.child_tid);
|
||||
NewThread->Thread->ThreadManager.clear_child_tid = reinterpret_cast<int32_t*>(args->args.child_tid);
|
||||
}
|
||||
|
||||
// clone3 flag
|
||||
@ -148,7 +147,7 @@ CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Fr
|
||||
return NewThread;
|
||||
}
|
||||
|
||||
uint64_t HandleNewClone(FEXCore::Core::InternalThreadState* Thread, FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame,
|
||||
uint64_t HandleNewClone(FEX::HLE::ThreadStateObject* Thread, FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame,
|
||||
FEX::HLE::clone3_args* CloneArgs) {
|
||||
auto GuestArgs = &CloneArgs->args;
|
||||
uint64_t flags = GuestArgs->flags;
|
||||
@ -172,7 +171,7 @@ uint64_t HandleNewClone(FEXCore::Core::InternalThreadState* Thread, FEXCore::Con
|
||||
|
||||
// CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID, CLONE_PIDFD will be handled by kernel
|
||||
// Call execution thread directly since we already are on the new thread
|
||||
NewThread->StartRunning.NotifyAll(); // Clear the start running flag
|
||||
NewThread->Thread->StartRunning.NotifyAll(); // Clear the start running flag
|
||||
CreatedNewThreadObject = true;
|
||||
} else {
|
||||
// If we don't have CLONE_THREAD then we are effectively a fork
|
||||
@ -183,36 +182,36 @@ uint64_t HandleNewClone(FEXCore::Core::InternalThreadState* Thread, FEXCore::Con
|
||||
|
||||
::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &CloneArgs->SignalMask, nullptr, sizeof(CloneArgs->SignalMask));
|
||||
|
||||
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RAX] = 0;
|
||||
Thread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RAX] = 0;
|
||||
if (GuestArgs->stack == 0) {
|
||||
// Copies in the original thread's stack
|
||||
} else {
|
||||
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = GuestArgs->stack;
|
||||
Thread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = GuestArgs->stack;
|
||||
}
|
||||
}
|
||||
|
||||
if (CloneArgs->Type == TYPE_CLONE3) {
|
||||
// If we are coming from a clone3 handler then we need to adjust RSP.
|
||||
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] += CloneArgs->args.stack_size;
|
||||
Thread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] += CloneArgs->args.stack_size;
|
||||
}
|
||||
|
||||
if (FEX::HLE::_SyscallHandler->Is64BitMode()) {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x64::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(GuestArgs->tls));
|
||||
x64::SetThreadArea(NewThread->Thread->CurrentFrame, reinterpret_cast<void*>(GuestArgs->tls));
|
||||
}
|
||||
// Set us to start just after the syscall instruction
|
||||
x64::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
x64::AdjustRipForNewThread(NewThread->Thread->CurrentFrame);
|
||||
} else {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x32::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(GuestArgs->tls));
|
||||
x32::SetThreadArea(NewThread->Thread->CurrentFrame, reinterpret_cast<void*>(GuestArgs->tls));
|
||||
}
|
||||
x32::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
x32::AdjustRipForNewThread(NewThread->Thread->CurrentFrame);
|
||||
}
|
||||
|
||||
// Depending on clone settings, our TID and PID could have changed
|
||||
Thread->ThreadManager.TID = FHU::Syscalls::gettid();
|
||||
Thread->ThreadManager.PID = ::getpid();
|
||||
FEX::HLE::_SyscallHandler->FM.UpdatePID(Thread->ThreadManager.PID);
|
||||
Thread->Thread->ThreadManager.TID = FHU::Syscalls::gettid();
|
||||
Thread->Thread->ThreadManager.PID = ::getpid();
|
||||
FEX::HLE::_SyscallHandler->FM.UpdatePID(Thread->Thread->ThreadManager.PID);
|
||||
|
||||
if (CreatedNewThreadObject) {
|
||||
FEX::HLE::_SyscallHandler->TM.TrackThread(Thread);
|
||||
@ -222,12 +221,12 @@ uint64_t HandleNewClone(FEXCore::Core::InternalThreadState* Thread, FEXCore::Con
|
||||
|
||||
// Start exuting the thread directly
|
||||
// Our host clone starts in a new stack space, so it can't return back to the JIT space
|
||||
CTX->ExecutionThread(Thread);
|
||||
CTX->ExecutionThread(Thread->Thread);
|
||||
|
||||
FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread);
|
||||
|
||||
// The rest of the context remains as is and the thread will continue executing
|
||||
return Thread->StatusCode;
|
||||
return Thread->Thread->StatusCode;
|
||||
}
|
||||
|
||||
uint64_t ForkGuest(FEXCore::Core::InternalThreadState* Thread, FEXCore::Core::CpuStateFrame* Frame, uint32_t flags, void* stack,
|
||||
@ -386,6 +385,7 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
|
||||
// TLS/DTV teardown is something FEX can't control. Disable glibc checking when we leave a pthread.
|
||||
// Since this thread is hard stopping, we can't track the TLS/DTV teardown in FEX's thread handling.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable();
|
||||
auto ThreadObject = static_cast<FEX::HLE::ThreadStateObject*>(Thread->FrontendPtr);
|
||||
|
||||
if (Thread->ThreadManager.clear_child_tid) {
|
||||
std::atomic<uint32_t>* Addr = reinterpret_cast<std::atomic<uint32_t>*>(Thread->ThreadManager.clear_child_tid);
|
||||
@ -394,7 +394,7 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
|
||||
}
|
||||
|
||||
Thread->StatusCode = status;
|
||||
FEX::HLE::_SyscallHandler->TM.StopThread(Thread);
|
||||
FEX::HLE::_SyscallHandler->TM.StopThread(ThreadObject);
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
@ -15,9 +15,10 @@ struct CPUState;
|
||||
} // namespace FEXCore::Core
|
||||
|
||||
namespace FEX::HLE {
|
||||
FEXCore::Core::InternalThreadState*
|
||||
CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args* args);
|
||||
uint64_t HandleNewClone(FEXCore::Core::InternalThreadState* Thread, FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame,
|
||||
struct ThreadStateObject;
|
||||
|
||||
FEX::HLE::ThreadStateObject* CreateNewThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::clone3_args* args);
|
||||
uint64_t HandleNewClone(FEX::HLE::ThreadStateObject* Thread, FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame,
|
||||
FEX::HLE::clone3_args* GuestArgs);
|
||||
uint64_t ForkGuest(FEXCore::Core::InternalThreadState* Thread, FEXCore::Core::CpuStateFrame* Frame, uint32_t flags, void* stack,
|
||||
size_t StackSize, pid_t* parent_tid, pid_t* child_tid, void* tls);
|
||||
|
@ -6,15 +6,17 @@
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
|
||||
namespace FEX::HLE {
|
||||
FEXCore::Core::InternalThreadState*
|
||||
FEX::HLE::ThreadStateObject*
|
||||
ThreadManager::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::Core::CPUState* NewThreadState, uint64_t ParentTID) {
|
||||
auto Thread = CTX->CreateThread(InitialRIP, StackPointer, NewThreadState, ParentTID);
|
||||
auto ThreadStateObject = new FEX::HLE::ThreadStateObject;
|
||||
ThreadStateObject->Thread = CTX->CreateThread(InitialRIP, StackPointer, NewThreadState, ParentTID);
|
||||
ThreadStateObject->Thread->FrontendPtr = ThreadStateObject;
|
||||
|
||||
++IdleWaitRefCount;
|
||||
return Thread;
|
||||
return ThreadStateObject;
|
||||
}
|
||||
|
||||
void ThreadManager::DestroyThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void ThreadManager::DestroyThread(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall) {
|
||||
{
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
auto It = std::find(Threads.begin(), Threads.end(), Thread);
|
||||
@ -22,32 +24,33 @@ void ThreadManager::DestroyThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
Threads.erase(It);
|
||||
}
|
||||
|
||||
HandleThreadDeletion(Thread);
|
||||
HandleThreadDeletion(Thread, NeedsTLSUninstall);
|
||||
}
|
||||
|
||||
void ThreadManager::StopThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
if (Thread->RunningEvents.Running.exchange(false)) {
|
||||
SignalDelegation->SignalThread(Thread, FEXCore::Core::SignalEvent::Stop);
|
||||
void ThreadManager::StopThread(FEX::HLE::ThreadStateObject* Thread) {
|
||||
if (Thread->Thread->RunningEvents.Running.exchange(false)) {
|
||||
SignalDelegation->SignalThread(Thread->Thread, FEXCore::Core::SignalEvent::Stop);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadManager::RunThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void ThreadManager::RunThread(FEX::HLE::ThreadStateObject* Thread) {
|
||||
// Tell the thread to start executing
|
||||
Thread->StartRunning.NotifyAll();
|
||||
Thread->Thread->StartRunning.NotifyAll();
|
||||
}
|
||||
|
||||
void ThreadManager::HandleThreadDeletion(FEXCore::Core::InternalThreadState* Thread) {
|
||||
if (Thread->ExecutionThread) {
|
||||
if (Thread->ExecutionThread->joinable()) {
|
||||
Thread->ExecutionThread->join(nullptr);
|
||||
void ThreadManager::HandleThreadDeletion(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall) {
|
||||
if (Thread->Thread->ExecutionThread) {
|
||||
if (Thread->Thread->ExecutionThread->joinable()) {
|
||||
Thread->Thread->ExecutionThread->join(nullptr);
|
||||
}
|
||||
|
||||
if (Thread->ExecutionThread->IsSelf()) {
|
||||
Thread->ExecutionThread->detach();
|
||||
if (Thread->Thread->ExecutionThread->IsSelf()) {
|
||||
Thread->Thread->ExecutionThread->detach();
|
||||
}
|
||||
}
|
||||
|
||||
CTX->DestroyThread(Thread);
|
||||
CTX->DestroyThread(Thread->Thread, NeedsTLSUninstall);
|
||||
delete Thread;
|
||||
--IdleWaitRefCount;
|
||||
IdleWaitCV.notify_all();
|
||||
}
|
||||
@ -56,7 +59,7 @@ void ThreadManager::NotifyPause() {
|
||||
// Tell all the threads that they should pause
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
for (auto& Thread : Threads) {
|
||||
SignalDelegation->SignalThread(Thread, FEXCore::Core::SignalEvent::Pause);
|
||||
SignalDelegation->SignalThread(Thread->Thread, FEXCore::Core::SignalEvent::Pause);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,11 +72,11 @@ void ThreadManager::Run() {
|
||||
// Spin up all the threads
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
for (auto& Thread : Threads) {
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Return);
|
||||
Thread->Thread->SignalReason.store(FEXCore::Core::SignalEvent::Return);
|
||||
}
|
||||
|
||||
for (auto& Thread : Threads) {
|
||||
Thread->StartRunning.NotifyAll();
|
||||
Thread->Thread->StartRunning.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +116,7 @@ void ThreadManager::Step() {
|
||||
// Walk the threads and tell them to clear their caches
|
||||
// Useful when our block size is set to a large number and we need to step a single instruction
|
||||
for (auto& Thread : Threads) {
|
||||
CTX->ClearCodeCache(Thread);
|
||||
CTX->ClearCodeCache(Thread->Thread);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,30 +129,30 @@ void ThreadManager::Step() {
|
||||
|
||||
void ThreadManager::Stop(bool IgnoreCurrentThread) {
|
||||
pid_t tid = FHU::Syscalls::gettid();
|
||||
FEXCore::Core::InternalThreadState* CurrentThread {};
|
||||
FEX::HLE::ThreadStateObject* CurrentThread {};
|
||||
|
||||
// Tell all the threads that they should stop
|
||||
{
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
for (auto& Thread : Threads) {
|
||||
if (IgnoreCurrentThread && Thread->ThreadManager.TID == tid) {
|
||||
if (IgnoreCurrentThread && Thread->Thread->ThreadManager.TID == tid) {
|
||||
// If we are callign stop from the current thread then we can ignore sending signals to this thread
|
||||
// This means that this thread is already gone
|
||||
} else if (Thread->ThreadManager.TID == tid) {
|
||||
} else if (Thread->Thread->ThreadManager.TID == tid) {
|
||||
// We need to save the current thread for last to ensure all threads receive their stop signals
|
||||
CurrentThread = Thread;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Thread->RunningEvents.Running.load()) {
|
||||
if (Thread->Thread->RunningEvents.Running.load()) {
|
||||
StopThread(Thread);
|
||||
}
|
||||
|
||||
// If the thread is waiting to start but immediately killed then there can be a hang
|
||||
// This occurs in the case of gdb attach with immediate kill
|
||||
if (Thread->RunningEvents.WaitingToStart.load()) {
|
||||
Thread->RunningEvents.EarlyExit = true;
|
||||
Thread->StartRunning.NotifyAll();
|
||||
if (Thread->Thread->RunningEvents.WaitingToStart.load()) {
|
||||
Thread->Thread->RunningEvents.EarlyExit = true;
|
||||
Thread->Thread->StartRunning.NotifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,12 +189,12 @@ void ThreadManager::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThre
|
||||
// This function is called after fork
|
||||
// We need to cleanup some of the thread data that is dead
|
||||
for (auto& DeadThread : Threads) {
|
||||
if (DeadThread == LiveThread) {
|
||||
if (DeadThread->Thread == LiveThread) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setting running to false ensures that when they are shutdown we won't send signals to kill them
|
||||
DeadThread->RunningEvents.Running = false;
|
||||
DeadThread->Thread->RunningEvents.Running = false;
|
||||
|
||||
// Despite what google searches may susgest, glibc actually has special code to handle forks
|
||||
// with multiple active threads.
|
||||
@ -202,7 +205,8 @@ void ThreadManager::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThre
|
||||
|
||||
// Deconstructing the Interneal thread state should clean up most of the state.
|
||||
// But if anything on the now deleted stack is holding a refrence to the heap, it will be leaked
|
||||
CTX->DestroyThread(DeadThread);
|
||||
CTX->DestroyThread(DeadThread->Thread);
|
||||
delete DeadThread;
|
||||
|
||||
// FIXME: Make sure sure nothing gets leaked via the heap. Ideas:
|
||||
// * Make sure nothing is allocated on the heap without ref in InternalThreadState
|
||||
@ -212,7 +216,9 @@ void ThreadManager::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThre
|
||||
|
||||
// Remove all threads but the live thread from Threads
|
||||
Threads.clear();
|
||||
Threads.push_back(LiveThread);
|
||||
|
||||
auto LiveThreadData = static_cast<FEX::HLE::ThreadStateObject*>(LiveThread->FrontendPtr);
|
||||
Threads.push_back(LiveThreadData);
|
||||
|
||||
// Clean up dead stacks
|
||||
FEXCore::Threads::Thread::CleanupAfterFork();
|
||||
|
@ -18,6 +18,9 @@ namespace FEX::HLE {
|
||||
class SyscallHandler;
|
||||
class SignalDelegator;
|
||||
|
||||
struct ThreadStateObject : public FEXCore::Allocator::FEXAllocOperators {
|
||||
FEXCore::Core::InternalThreadState* Thread;
|
||||
};
|
||||
|
||||
class ThreadManager final {
|
||||
public:
|
||||
@ -27,16 +30,16 @@ public:
|
||||
|
||||
~ThreadManager();
|
||||
|
||||
FEXCore::Core::InternalThreadState*
|
||||
FEX::HLE::ThreadStateObject*
|
||||
CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::Core::CPUState* NewThreadState = nullptr, uint64_t ParentTID = 0);
|
||||
void TrackThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void TrackThread(FEX::HLE::ThreadStateObject* Thread) {
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
Threads.emplace_back(Thread);
|
||||
}
|
||||
|
||||
void DestroyThread(FEXCore::Core::InternalThreadState* Thread);
|
||||
void StopThread(FEXCore::Core::InternalThreadState* Thread);
|
||||
void RunThread(FEXCore::Core::InternalThreadState* Thread);
|
||||
void DestroyThread(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall = false);
|
||||
void StopThread(FEX::HLE::ThreadStateObject* Thread);
|
||||
void RunThread(FEX::HLE::ThreadStateObject* Thread);
|
||||
|
||||
void Pause();
|
||||
void Run();
|
||||
@ -64,7 +67,7 @@ public:
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
|
||||
|
||||
for (auto& Thread : Threads) {
|
||||
CTX->InvalidateGuestCodeRange(Thread, Start, Length);
|
||||
CTX->InvalidateGuestCodeRange(Thread->Thread, Start, Length);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,11 +81,11 @@ public:
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
|
||||
|
||||
for (auto& Thread : Threads) {
|
||||
CTX->InvalidateGuestCodeRange(Thread, Start, Length, callback);
|
||||
CTX->InvalidateGuestCodeRange(Thread->Thread, Start, Length, callback);
|
||||
}
|
||||
}
|
||||
|
||||
const fextl::vector<FEXCore::Core::InternalThreadState*>* GetThreads() const {
|
||||
const fextl::vector<FEX::HLE::ThreadStateObject*>* GetThreads() const {
|
||||
return &Threads;
|
||||
}
|
||||
|
||||
@ -91,7 +94,7 @@ private:
|
||||
FEX::HLE::SignalDelegator* SignalDelegation;
|
||||
|
||||
FEXCore::ForkableUniqueMutex ThreadCreationMutex;
|
||||
fextl::vector<FEXCore::Core::InternalThreadState*> Threads;
|
||||
fextl::vector<FEX::HLE::ThreadStateObject*> Threads;
|
||||
|
||||
// Thread idling support.
|
||||
bool Running {};
|
||||
@ -99,7 +102,7 @@ private:
|
||||
std::condition_variable IdleWaitCV;
|
||||
std::atomic<uint32_t> IdleWaitRefCount {};
|
||||
|
||||
void HandleThreadDeletion(FEXCore::Core::InternalThreadState* Thread);
|
||||
void HandleThreadDeletion(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall = false);
|
||||
void NotifyPause();
|
||||
};
|
||||
|
||||
|
@ -301,7 +301,8 @@ int main(int argc, char** argv, char** const envp) {
|
||||
if (!CTX->InitCore()) {
|
||||
return 1;
|
||||
}
|
||||
auto ParentThread = CTX->CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
|
||||
auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
|
||||
SyscallHandler->TM.TrackThread(ParentThread);
|
||||
SignalDelegation->RegisterTLSState(ParentThread);
|
||||
|
||||
if (!ParentThread) {
|
||||
@ -310,22 +311,23 @@ int main(int argc, char** argv, char** const envp) {
|
||||
|
||||
int LongJumpVal = setjmp(LongJumpHandler::LongJump);
|
||||
if (!LongJumpVal) {
|
||||
CTX->RunUntilExit(ParentThread);
|
||||
CTX->RunUntilExit(ParentThread->Thread);
|
||||
}
|
||||
|
||||
// Just re-use compare state. It also checks against the expected values in config.
|
||||
memcpy(&State, &ParentThread->CurrentFrame->State, sizeof(State));
|
||||
|
||||
SyscallHandler.reset();
|
||||
memcpy(&State, &ParentThread->Thread->CurrentFrame->State, sizeof(State));
|
||||
|
||||
SignalDelegation->UninstallTLSState(ParentThread);
|
||||
CTX->DestroyThread(ParentThread, true);
|
||||
FEX::HLE::_SyscallHandler->TM.DestroyThread(ParentThread, true);
|
||||
|
||||
SyscallHandler.reset();
|
||||
}
|
||||
#ifndef _WIN32
|
||||
else {
|
||||
// Run as host
|
||||
SupportsAVX = true;
|
||||
SignalDelegation->RegisterTLSState((FEXCore::Core::InternalThreadState*)UINTPTR_MAX);
|
||||
FEX::HLE::ThreadStateObject ThreadStateObject {};
|
||||
SignalDelegation->RegisterTLSState(&ThreadStateObject);
|
||||
if (!Loader.MapMemory()) {
|
||||
// failed to map
|
||||
LogMan::Msg::EFmt("Failed to map {}-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
|
||||
@ -333,6 +335,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
}
|
||||
|
||||
RunAsHost(SignalDelegation, Loader.DefaultRIP(), Loader.GetStackPointer(), &State);
|
||||
SignalDelegation->UninstallTLSState(&ThreadStateObject);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user