MCPcopy Create free account
hub / github.com/FEX-Emu/FEX / BTCpuSuspendLocalThread

Function BTCpuSuspendLocalThread

Source/Windows/WOW64/Module.cpp:780–865  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

778}
779
780NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
781 if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_SUSPEND_RESUME)) {
782 return STATUS_ACCESS_DENIED;
783 }
784
785 auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
786 THREAD_BASIC_INFORMATION Info;
787 if (NTSTATUS Err = NtQueryInformationThread(*ThreadDup, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
788 return Err;
789 }
790
791 const auto ThreadTID = reinterpret_cast<uint64_t>(Info.ClientId.UniqueThread);
792 if (ThreadTID == GetCurrentThreadId()) {
793 LogMan::Msg::DFmt("Suspending self");
794 // Mark the CPU area as dirty, to force the JIT context to be restored from it on entry as it may be changed using
795 // SetThreadContext (which doesn't use the BTCpu API)
796 if (!(GetTLS().ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
797 if (NTSTATUS Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
798 return Err;
799 }
800 }
801
802 return NtSuspendThread(*ThreadDup, Count);
803 }
804
805 LogMan::Msg::DFmt("Suspending thread: {:X}", ThreadTID);
806
807 auto [Err, TLS] = GetThreadTLS(*ThreadDup);
808 if (Err) {
809 return Err;
810 }
811
812 std::scoped_lock Lock(ThreadCreationMutex);
813
814 // If the thread hasn't yet been initialized, suspend it without special handling as it wont yet have entered the JIT
815 if (!Threads.contains(ThreadTID)) {
816 LogMan::Msg::DFmt("Thread suspended: {:X}", ThreadTID);
817 return NtSuspendThread(*ThreadDup, Count);
818 }
819
820 // If CONTROL_IN_JIT is unset at this point, then it can never be set (and thus the JIT cannot be reentered) as
821 // CONTROL_PAUSED has been set, as such, while this may redundantly request interrupts in rare cases it will never
822 // miss them
823 if (TLS.ControlWord().fetch_or(ControlBits::PAUSED, std::memory_order::relaxed) & ControlBits::IN_JIT) {
824 LogMan::Msg::DFmt("Thread {:X} is in JIT, polling for interrupt", ThreadTID);
825
826 ULONG TmpProt;
827 void* TmpAddress = &TLS.ThreadState()->InterruptFaultPage;
828 SIZE_T TmpSize = FEXCore::Utils::FEX_PAGE_SIZE;
829 NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_READONLY, &TmpProt);
830 }
831
832 // Spin until the JIT is interrupted
833 while (TLS.ControlWord().load() & ControlBits::IN_JIT)
834 ;
835
836 // The JIT has now been interrupted and the context stored in the thread's CPU area is up-to-date
837 if (Err = NtSuspendThread(*ThreadDup, Count); Err) {

Callers

nothing calls this directly

Calls 6

ValidateHandleAccessFunction · 0.85
DupHandleFunction · 0.85
DFmtFunction · 0.85
GetTLSFunction · 0.85
FlushThreadStateContextFunction · 0.85
GetThreadTLSFunction · 0.85

Tested by

no test coverage detected