Windows: Avoid potential race between thread init and termination

With the prior locking, we could try to terminate a thread while it
was initializing and perhaps holding global locks leading to deadlock.
This commit is contained in:
Billy Laws committed 2025-08-12 00:51:00 +01:00
1 parent 972c8a97dc
commit 4df17abea7
2 files changed
+25 -25

No files matched your search

+12 -12
View File
@@ -911,6 +911,7 @@ void BTCpu64NotifyMemoryDirty(void* Address, SIZE_T Size) {
void BTCpu64NotifyReadFile(HANDLE Handle, void* Address, SIZE_T Size, BOOL After, NTSTATUS Status) {}
NTSTATUS ThreadInit() {
std::scoped_lock Lock(ThreadCreationMutex);
FEX::Windows::InitCRTThread();
static constexpr size_t EmulatorStackSize = 0x40000;
const uint64_t EmulatorStack = reinterpret_cast<uint64_t>(::VirtualAlloc(nullptr, EmulatorStackSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
@@ -945,7 +946,6 @@ NTSTATUS ThreadInit() {
Exception::LoadStateFromECContext(Thread, CPUArea.ContextAmd64().AMD64_Context);
{
std::scoped_lock Lock(ThreadCreationMutex);
auto ThreadTID = GetCurrentThreadId();
Threads.emplace(ThreadTID, Thread);
if (StatAllocHandler) {
@@ -959,29 +959,29 @@ NTSTATUS ThreadInit() {
}
NTSTATUS ThreadTerm(HANDLE Thread, LONG ExitCode) {
const auto [Err, CPUArea] = GetThreadCPUArea(Thread);
if (Err) {
return Err;
}
auto* OldThreadState = CPUArea.ThreadState();
CPUArea.ThreadState() = nullptr;
THREAD_BASIC_INFORMATION Info;
if (NTSTATUS Err = NtQueryInformationThread(Thread, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
if (auto Err = NtQueryInformationThread(Thread, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
return Err;
}
const auto ThreadTID = reinterpret_cast<uint64_t>(Info.ClientId.UniqueThread);
bool Self = ThreadTID == GetCurrentThreadId();
const auto [Err, CPUArea] = GetThreadCPUArea(Thread);
if (Err) {
return Err;
}
{
std::scoped_lock Lock(ThreadCreationMutex);
Threads.erase(ThreadTID);
if (StatAllocHandler) {
StatAllocHandler->DeallocateSlot(OldThreadState->ThreadStats);
StatAllocHandler->DeallocateSlot(CPUArea.ThreadState()->ThreadStats);
}
}
FEX::Windows::CallRetStack::DestroyThread(OldThreadState);
CTX->DestroyThread(OldThreadState);
FEX::Windows::CallRetStack::DestroyThread(CPUArea.ThreadState());
CTX->DestroyThread(CPUArea.ThreadState());
::VirtualFree(reinterpret_cast<void*>(CPUArea.EmulatorStackLimit()), 0, MEM_RELEASE);
if (ThreadTID == GetCurrentThreadId()) {
FEX::Windows::DeinitCRTThread();