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:
Ryan Houdek 2024-05-05 07:06:37 -07:00
parent 729e32ccc2
commit d372552593
No known key found for this signature in database
10 changed files with 132 additions and 119 deletions

View File

@ -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();

View File

@ -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")) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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;
});

View File

@ -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);

View File

@ -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();

View File

@ -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();
};

View File

@ -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