| 632 | } |
| 633 | |
| 634 | void BTCpuThreadTerm(HANDLE Thread, LONG ExitCode) { |
| 635 | if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_TERMINATE)) { |
| 636 | return; |
| 637 | } |
| 638 | |
| 639 | auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME); |
| 640 | |
| 641 | THREAD_BASIC_INFORMATION Info; |
| 642 | if (auto Err = NtQueryInformationThread(*ThreadDup, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) { |
| 643 | return; |
| 644 | } |
| 645 | |
| 646 | const auto ThreadTID = reinterpret_cast<uint64_t>(Info.ClientId.UniqueThread); |
| 647 | bool Self = ThreadTID == GetCurrentThreadId(); |
| 648 | if (!Self) { |
| 649 | // If we are suspending a thread that isn't ourselves, try to suspend it first so we know internal JIT locks aren't being held. |
| 650 | RtlWow64SuspendThread(*ThreadDup, NULL); |
| 651 | } |
| 652 | |
| 653 | auto [Err, TLS] = GetThreadTLS(*ThreadDup); |
| 654 | if (Err) { |
| 655 | return; |
| 656 | } |
| 657 | |
| 658 | { |
| 659 | std::scoped_lock Lock(ThreadCreationMutex); |
| 660 | auto it = Threads.find(ThreadTID); |
| 661 | if (it == Threads.end()) { |
| 662 | // Thread already terminated |
| 663 | return; |
| 664 | } |
| 665 | |
| 666 | Threads.erase(it); |
| 667 | if (StatAllocHandler) { |
| 668 | StatAllocHandler->DeallocateSlot(TLS.ThreadState()->ThreadStats); |
| 669 | } |
| 670 | } |
| 671 | auto ThreadState = TLS.ThreadState(); |
| 672 | |
| 673 | // GDT and LDT are mirrored, only free one. |
| 674 | delete[] ThreadState->CurrentFrame->State.segment_arrays[FEXCore::Core::CPUState::SEGMENT_ARRAY_INDEX_GDT]; |
| 675 | |
| 676 | FEX::Windows::CallRetStack::DestroyThread(ThreadState); |
| 677 | CTX->DestroyThread(ThreadState); |
| 678 | if (Self) { |
| 679 | FEX::Windows::DeinitCRTThread(); |
| 680 | } |
| 681 | } |
| 682 | |
| 683 | void* BTCpuGetBopCode() { |
| 684 | return BridgeInstrs::Syscall; |
nothing calls this directly
no test coverage detected