diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index f63c420d7a02b..c646bada45e01 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -490,5 +490,37 @@ private void ResetFinalizerThreadSlow() Priority = ThreadPriority.Highest; } } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_PollGC")] + private static partial void ThreadNative_PollGC(); + + // GC Suspension is done by simply dropping into native code via p/invoke, and we reuse the p/invoke + // mechanism for suspension. On all architectures we should have the actual stub used for the check be implemented + // as a small assembly stub which checks the global g_TrapReturningThreads flag and tail-call to this helper + private static unsafe void PollGC() + { + NativeThreadState catchAtSafePoint = ((NativeThreadClass*)Thread.DirectOnThreadLocalData.pNativeThread)->m_State & NativeThreadState.TS_CatchAtSafePoint; + if (catchAtSafePoint != NativeThreadState.None) + { + ThreadNative_PollGC(); + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct NativeThreadClass + { + public NativeThreadState m_State; + } + + private enum NativeThreadState + { + None = 0, + TS_AbortRequested = 0x00000001, // Abort the thread + TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads? + TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only) + + // We require (and assert) that the following bits are less than 0x100. + TS_CatchAtSafePoint = (TS_AbortRequested | TS_DebugSuspendPending | TS_GCOnTransitions), + }; } } diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index aabc86d3cd65a..e9422591ed8d4 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -433,7 +433,6 @@ enum CorInfoHelpFunc CORINFO_HELP_STOP_FOR_GC, // Call GC (force a GC) CORINFO_HELP_POLL_GC, // Ask GC if it wants to collect - CORINFO_HELP_STRESS_GC, // Force a GC, but then update the JITTED code to be a noop call CORINFO_HELP_CHECK_OBJ, // confirm that ECX is a valid object pointer (debugging only) /* GC Write barrier support */ diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 8b97677ea11f9..d5321c076066c 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -149,9 +149,8 @@ // GC support DYNAMICJITHELPER(CORINFO_HELP_STOP_FOR_GC, JIT_RareDisableHelper, METHOD__NIL) - JITHELPER(CORINFO_HELP_POLL_GC, JIT_PollGC, METHOD__NIL) - JITHELPER(CORINFO_HELP_STRESS_GC, JIT_StressGC, METHOD__NIL) - + DYNAMICJITHELPER(CORINFO_HELP_POLL_GC, JIT_PollGC, METHOD__THREAD__POLLGC) + JITHELPER(CORINFO_HELP_CHECK_OBJ, JIT_CheckObj, METHOD__NIL) // GC Write barrier support diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index dbb365e68cd66..04bca41e47667 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -124,7 +124,6 @@ which is the right helper to use to allocate an object of a given type. */ CORINFO_HELP_STOP_FOR_GC, // Call GC (force a GC) CORINFO_HELP_POLL_GC, // Ask GC if it wants to collect - CORINFO_HELP_STRESS_GC, // Force a GC, but then update the JITTED code to be a noop call CORINFO_HELP_CHECK_OBJ, // confirm that ECX is a valid object pointer (debugging only) /* GC Write barrier support */ diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index 18435be1b1372..15b4cb4cca09e 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -12,6 +12,8 @@ extern ProfileTailcall:proc extern OnHijackWorker:proc extern JIT_RareDisableHelperWorker:proc +extern g_pPollGC:QWORD +extern g_TrapReturningThreads:DWORD ; EXTERN_C int __fastcall HelperMethodFrameRestoreState( ; INDEBUG_COMMA(HelperMethodFrame *pFrame) @@ -447,5 +449,13 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT endif ; FEATURE_TIERED_COMPILATION - end +LEAF_ENTRY JIT_PollGC, _TEXT + cmp [g_TrapReturningThreads], 0 + jnz RarePath + ret +RarePath: + mov rax, g_pPollGC + TAILJMP_RAX +LEAF_END JIT_PollGC, _TEXT + end \ No newline at end of file diff --git a/src/coreclr/vm/amd64/asmhelpers.S b/src/coreclr/vm/amd64/asmhelpers.S index 8d83938246a2c..f5830b2df8317 100644 --- a/src/coreclr/vm/amd64/asmhelpers.S +++ b/src/coreclr/vm/amd64/asmhelpers.S @@ -311,3 +311,14 @@ LEAF_ENTRY GetTlsIndexObjectDescOffset, _TEXT int 3 LEAF_END GetTlsIndexObjectDescOffset, _TEXT #endif + +LEAF_ENTRY JIT_PollGC, _TEXT + PREPARE_EXTERNAL_VAR g_TrapReturningThreads, rax + cmp dword ptr [rax], 0 + jnz LOCAL_LABEL(JIT_PollGCRarePath) + ret +LOCAL_LABEL(JIT_PollGCRarePath): + PREPARE_EXTERNAL_VAR g_pPollGC, rax + mov rax, [rax] + jmp rax +LEAF_END JIT_PollGC, _TEXT diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index a1bf37c295ab9..e12753754b6bc 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -606,5 +606,6 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool // #define JIT_GetDynamicGCStaticBase JIT_GetDynamicGCStaticBase_SingleAppDomain #define JIT_GetDynamicNonGCStaticBase JIT_GetDynamicNonGCStaticBase_SingleAppDomain +#define JIT_PollGC JIT_PollGC #endif // __cgencpu_h__ diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index a3fb8bc3e9a4e..537d7f57d0fa8 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1006,6 +1006,8 @@ extern "C" PCODE g_pGetGCStaticBase; PCODE g_pGetGCStaticBase; extern "C" PCODE g_pGetNonGCStaticBase; PCODE g_pGetNonGCStaticBase; +extern "C" PCODE g_pPollGC; +PCODE g_pPollGC; void SystemDomain::LoadBaseSystemClasses() { @@ -1144,6 +1146,7 @@ void SystemDomain::LoadBaseSystemClasses() g_pGetGCStaticBase = CoreLibBinder::GetMethod(METHOD__STATICSHELPERS__GET_GC_STATIC)->GetMultiCallableAddrOfCode(); g_pGetNonGCStaticBase = CoreLibBinder::GetMethod(METHOD__STATICSHELPERS__GET_NONGC_STATIC)->GetMultiCallableAddrOfCode(); + g_pPollGC = CoreLibBinder::GetMethod(METHOD__THREAD__POLLGC)->GetMultiCallableAddrOfCode(); #ifdef PROFILING_SUPPORTED // Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index b970aaf28f07c..403673c11f294 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -914,3 +914,27 @@ ProbeLoop: NESTED_END OnCallCountThresholdReachedStub, _TEXT #endif // FEATURE_TIERED_COMPILATION + + LEAF_ENTRY JIT_PollGC, _TEXT +#if defined(__clang__) + ldr r2, =g_TrapReturningThreads-(1f+4) +1: + add r2, pc +#else + ldr r2, =g_TrapReturningThreads +#endif + PREPARE_EXTERNAL_VAR g_TrapReturningThreads, r0 + ldr r2, [r2] + cbnz r2, LOCAL_LABEL(JIT_PollGCRarePath) + ret +LOCAL_LABEL(JIT_PollGCRarePath): +#if defined(__clang__) + ldr r2, =g_pPollGC-(1f+4) +1: + add r2, pc +#else + ldr r2, =g_pPollGC +#endif + ldr r2, [r2] + EPILOG_BRANCH_REG r2 + LEAF_END JIT_PollGC, _TEXT diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h index 712d3454132d3..4f39b82bd1342 100644 --- a/src/coreclr/vm/arm/cgencpu.h +++ b/src/coreclr/vm/arm/cgencpu.h @@ -102,6 +102,8 @@ EXTERN_C void setFPReturn(int fpSize, INT64 retVal); #define FLOAT_REGISTER_SIZE 4 // each register in FloatArgumentRegisters is 4 bytes. +#define JIT_PollGC JIT_PollGC + //********************************************************************** // Parameter size //********************************************************************** diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 65c1eaec4121a..d40d210dfb03a 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -806,3 +806,14 @@ LEAF_ENTRY GetTLSResolverAddress, _TEXT LEAF_END GetTLSResolverAddress, _TEXT // ------------------------------------------------------------------ #endif // !TARGET_OSX + +LEAF_ENTRY JIT_PollGC, _TEXT + PREPARE_EXTERNAL_VAR g_TrapReturningThreads, x9 + ldr w9, [x9] + cbnz w9, LOCAL_LABEL(JIT_PollGCRarePath) + ret +LOCAL_LABEL(JIT_PollGCRarePath): + PREPARE_EXTERNAL_VAR g_pPollGC, x9 + ldr x9, [x9] + br x9 +LEAF_END JIT_PollGC, _TEXT diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index eea1e98df7527..80b6a240f1b67 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -39,6 +39,9 @@ IMPORT g_pGetGCStaticBase IMPORT g_pGetNonGCStaticBase + IMPORT g_pPollGC + IMPORT g_TrapReturningThreads + #ifdef WRITE_BARRIER_CHECK SETALIAS g_GCShadow, ?g_GCShadow@@3PEAEEA SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PEAEEA @@ -1179,6 +1182,17 @@ __HelperNakedFuncName SETS "$helper":CC:"Naked" #endif ; FEATURE_SPECIAL_USER_MODE_APC + LEAF_ENTRY JIT_PollGC + ldr x9, =g_TrapReturningThreads + ldr w9, [x9] + cbnz w9, RarePath + ret +RarePath + ldr x9, =g_pPollGC + ldr x9, [x9] + br x9 + LEAF_END + ; Must be at very end of file END diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index f83de42fecc36..dcfedece34c11 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -133,6 +133,7 @@ inline unsigned StackElemSize(unsigned parmSize, bool isValueType, bool isFloatH // #define JIT_GetDynamicGCStaticBase JIT_GetDynamicGCStaticBase_SingleAppDomain #define JIT_GetDynamicNonGCStaticBase JIT_GetDynamicNonGCStaticBase_SingleAppDomain +#define JIT_PollGC JIT_PollGC //********************************************************************** // Frames diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index e71fe3908786b..2e4170a81ef83 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -848,6 +848,12 @@ extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::Threa } #endif //FEATURE_COMINTEROP +extern "C" void QCALLTYPE ThreadNative_PollGC() +{ + // This is an intentional no-op. The call is made to ensure that the thread goes through a GC transition + // and is thus marked as a GC safe point, and that the p/invoke rare path will kick in +} + extern "C" BOOL QCALLTYPE ThreadNative_YieldThread() { QCALL_CONTRACT; diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index c06af82d0f296..9b17981e16e7a 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -50,6 +50,7 @@ extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle threa extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle thread, BOOL value); extern "C" void QCALLTYPE ThreadNative_InformThreadNameChange(QCall::ThreadHandle thread, LPCWSTR name, INT32 len); extern "C" BOOL QCALLTYPE ThreadNative_YieldThread(); +extern "C" void QCALLTYPE ThreadNative_PollGC(); extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId(); extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t); extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 7a5fe8d99743b..c2b431e0968bf 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -852,6 +852,8 @@ DEFINE_CLASS(DIRECTONTHREADLOCALDATA, Threading, Thread+DirectOnThreadLocalData) DEFINE_CLASS(THREAD, Threading, Thread) DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid) +DEFINE_METHOD(THREAD, POLLGC, PollGC, NoSig) + #ifdef FEATURE_OBJCMARSHAL DEFINE_CLASS(AUTORELEASEPOOL, Threading, AutoreleasePool) DEFINE_METHOD(AUTORELEASEPOOL, CREATEAUTORELEASEPOOL, CreateAutoreleasePool, SM_RetVoid) diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 4ea6e33341d0a..5a0feefb96dcb 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -726,7 +726,6 @@ class Frame : public FrameBase friend class TailCallFrame; friend class AppDomain; friend VOID RealCOMPlusThrow(OBJECTREF); - friend FCDECL0(VOID, JIT_StressGC); #ifdef _DEBUG friend LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo); #endif diff --git a/src/coreclr/vm/i386/PInvokeStubs.asm b/src/coreclr/vm/i386/PInvokeStubs.asm index 6f9ec4526a7ea..a0bc18d616bce 100644 --- a/src/coreclr/vm/i386/PInvokeStubs.asm +++ b/src/coreclr/vm/i386/PInvokeStubs.asm @@ -24,7 +24,7 @@ extern _s_gsCookie:DWORD extern ??_7InlinedCallFrame@@6B@:DWORD extern _g_TrapReturningThreads:DWORD -extern @JIT_PInvokeEndRarePath@0:proc +extern _JIT_PInvokeEndRarePath@0:proc .686P .XMM @@ -103,7 +103,7 @@ _JIT_PInvokeEnd@4 PROC public ret RarePath: - jmp @JIT_PInvokeEndRarePath@0 + jmp _JIT_PInvokeEndRarePath@0 _JIT_PInvokeEnd@4 ENDP diff --git a/src/coreclr/vm/i386/cgencpu.h b/src/coreclr/vm/i386/cgencpu.h index b87527be32333..7425254f22363 100644 --- a/src/coreclr/vm/i386/cgencpu.h +++ b/src/coreclr/vm/i386/cgencpu.h @@ -482,5 +482,8 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool // #define JIT_GetSharedNonGCStaticBase // #define JIT_GetSharedGCStaticBaseNoCtor // #define JIT_GetSharedNonGCStaticBaseNoCtor +#ifdef TARGET_WINDOWS +#define JIT_PollGC JIT_PollGC +#endif #endif // __cgenx86_h__ diff --git a/src/coreclr/vm/i386/jithelp.asm b/src/coreclr/vm/i386/jithelp.asm index 536f55e4f5809..1c8c057338342 100644 --- a/src/coreclr/vm/i386/jithelp.asm +++ b/src/coreclr/vm/i386/jithelp.asm @@ -44,6 +44,9 @@ JIT_TailCallVSDLeave TEXTEQU <_JIT_TailCallVSDLeave@0> JIT_TailCallHelper TEXTEQU <_JIT_TailCallHelper@4> JIT_TailCallReturnFromVSD TEXTEQU <_JIT_TailCallReturnFromVSD@0> +g_pPollGC TEXTEQU <_g_pPollGC> +g_TrapReturningThreads TEXTEQU <_g_TrapReturningThreads> + EXTERN g_ephemeral_low:DWORD EXTERN g_ephemeral_high:DWORD EXTERN g_lowest_address:DWORD @@ -59,6 +62,10 @@ EXTERN _g_TailCallFrameVptr:DWORD EXTERN @JIT_FailFast@0:PROC EXTERN _s_gsCookie:DWORD +EXTERN g_pPollGC:DWORD +EXTERN g_TrapReturningThreads:DWORD + + ifdef WRITE_BARRIER_CHECK ; Those global variables are always defined, but should be 0 for Server GC g_GCShadow TEXTEQU @@ -1149,4 +1156,13 @@ _JIT_StackProbe_End@0 PROC ret _JIT_StackProbe_End@0 ENDP +@JIT_PollGC@0 PROC public + cmp [g_TrapReturningThreads], 0 + jnz RarePath + ret +RarePath: + mov eax, g_pPollGC + jmp eax +@JIT_PollGC@0 ENDP + end diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index e116ff3b26994..780323637cf4b 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2410,96 +2410,62 @@ HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooksForJit(FunctionEnter3 * /*************************************************************/ // Slow helper to tailcall from the fast one -NOINLINE HCIMPL0(void, JIT_PollGC_Framed) +extern "C" void QCALLTYPE PollGC_Native() { - BEGIN_PRESERVE_LAST_ERROR; - - FCALL_CONTRACT; - FC_GC_POLL_NOT_NEEDED(); - - HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame -#ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); - } -#endif // _DEBUG - CommonTripThread(); // Indicate we are at a GC safe point -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); - } -#endif // _DEBUG - HELPER_METHOD_FRAME_END(); - END_PRESERVE_LAST_ERROR; + // Empty function to p/invoke into in order to allow the GC to suspend on transition } -HCIMPLEND - -HCIMPL0(VOID, JIT_PollGC) -{ - FCALL_CONTRACT; - - // As long as we can have GCPOLL_CALL polls, it would not hurt to check the trap flag. - if (!g_TrapReturningThreads) - return; - - // Does someone want this thread stopped? - if (!GetThread()->CatchAtSafePoint()) - return; - - // Tailcall to the slow helper - ENDFORBIDGC(); - HCCALL0(JIT_PollGC_Framed); -} -HCIMPLEND - /*************************************************************/ // This helper is similar to JIT_RareDisableHelper, but has more operations // tailored to the post-pinvoke operations. -extern "C" FCDECL0(VOID, JIT_PInvokeEndRarePath); +extern "C" VOID JIT_PInvokeEndRarePath(); -HCIMPL0(void, JIT_PInvokeEndRarePath) +void JIT_PInvokeEndRarePath() { BEGIN_PRESERVE_LAST_ERROR; - FCALL_CONTRACT; - Thread *thread = GetThread(); - // We need to disable the implicit FORBID GC region that exists inside an FCALL - // in order to call RareDisablePreemptiveGC(). - FC_CAN_TRIGGER_GC(); + // We execute RareDisablePreemptiveGC manually before checking any abort conditions + // as that operation may run the allocator, etc, and we need to be have have handled any suspensions requested + // by the GC before we reach that point. thread->RareDisablePreemptiveGC(); - FC_CAN_TRIGGER_GC_END(); - FC_GC_POLL_NOT_NEEDED(); - - HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame - thread->HandleThreadAbort(); - HELPER_METHOD_FRAME_END(); + if (thread->IsAbortRequested()) + { + // This function is acalled after a pinvoke finishes, in the rare case that either a GC + // or ThreadAbort is requested. This means that the pinvoke frame is still on the stack and + // enabled, but the thread has been marked as returning to cooperative mode. Thus we can + // use that frame to provide GC suspension safety, but we need to manually call EnablePreemptiveGC + // and DisablePreemptiveGC to put the function in a state where the BEGIN_QCALL/END_QCALL macros + // will work correctly. + thread->EnablePreemptiveGC(); + BEGIN_QCALL; + thread->HandleThreadAbort(); + END_QCALL; + thread->DisablePreemptiveGC(); + } thread->m_pFrame->Pop(thread); END_PRESERVE_LAST_ERROR; } -HCIMPLEND /*************************************************************/ // For an inlined N/Direct call (and possibly for other places that need this service) // we have noticed that the returning thread should trap for one reason or another. // ECall sets up the frame. -extern "C" FCDECL0(VOID, JIT_RareDisableHelper); +extern "C" VOID JIT_RareDisableHelper(); #if defined(TARGET_ARM) || defined(TARGET_AMD64) // The JIT expects this helper to preserve the return value on AMD64 and ARM. We should eventually // switch other platforms to the same convention since it produces smaller code. -extern "C" FCDECL0(VOID, JIT_RareDisableHelperWorker); +extern "C" VOID JIT_RareDisableHelperWorker(); -HCIMPL0(void, JIT_RareDisableHelperWorker) +void JIT_RareDisableHelperWorker() #else -HCIMPL0(void, JIT_RareDisableHelper) +void JIT_RareDisableHelper() #endif { // We do this here (before we set up a frame), because the following scenario @@ -2522,66 +2488,29 @@ HCIMPL0(void, JIT_RareDisableHelper) BEGIN_PRESERVE_LAST_ERROR; - FCALL_CONTRACT; - Thread *thread = GetThread(); - - // We need to disable the implicit FORBID GC region that exists inside an FCALL - // in order to call RareDisablePreemptiveGC(). - FC_CAN_TRIGGER_GC(); + // We execute RareDisablePreemptiveGC manually before checking any abort conditions + // as that operation may run the allocator, etc, and we need to be have have handled any suspensions requested + // by the GC before we reach that point. thread->RareDisablePreemptiveGC(); - FC_CAN_TRIGGER_GC_END(); - - FC_GC_POLL_NOT_NEEDED(); - HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame - thread->HandleThreadAbort(); - HELPER_METHOD_FRAME_END(); + if (thread->IsAbortRequested()) + { + // This function is acalled after a pinvoke finishes, in the rare case that either a GC + // or ThreadAbort is requested. This means that the pinvoke frame is still on the stack and + // enabled, but the thread has been marked as returning to cooperative mode. Thus we can + // use that frame to provide GC suspension safety, but we need to manually call EnablePreemptiveGC + // and DisablePreemptiveGC to put the function in a state where the BEGIN_QCALL/END_QCALL macros + // will work correctly. + thread->EnablePreemptiveGC(); + BEGIN_QCALL; + thread->HandleThreadAbort(); + END_QCALL; + thread->DisablePreemptiveGC(); + } END_PRESERVE_LAST_ERROR; } -HCIMPLEND - -/*********************************************************************/ -// This is called by the JIT after every instruction in fully interruptible -// code to make certain our GC tracking is OK -HCIMPL0(VOID, JIT_StressGC_NOP) -{ - FCALL_CONTRACT; -} -HCIMPLEND - - -HCIMPL0(VOID, JIT_StressGC) -{ - FCALL_CONTRACT; - -#ifdef _DEBUG - HELPER_METHOD_FRAME_BEGIN_0(); // Set up a frame - - bool fSkipGC = false; - - if (!fSkipGC) - GCHeapUtilities::GetGCHeap()->GarbageCollect(); - -// @TODO: the following ifdef is in error, but if corrected the -// compiler complains about the *__ms->pRetAddr() saying machine state -// doesn't allow -> -#ifdef _X86 - // Get the machine state, (from HELPER_METHOD_FRAME_BEGIN) - // and wack our return address to a nop function - BYTE* retInstrs = ((BYTE*) *__ms->pRetAddr()) - 4; - _ASSERTE(retInstrs[-1] == 0xE8); // it is a call instruction - // Wack it to point to the JITStressGCNop instead - InterlockedExchange((LONG*) retInstrs), (LONG) JIT_StressGC_NOP); -#endif // _X86 - - HELPER_METHOD_FRAME_END(); -#endif // _DEBUG -} -HCIMPLEND - - FCIMPL0(INT32, JIT_GetCurrentManagedThreadId) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 71012b72649d1..a99604cd66716 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -109,6 +109,12 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, // The portable helper is used if the platform does not provide optimized implementation. // +#ifdef JIT_PollGC +EXTERN_C FCDECL0(void, JIT_PollGC); +#else +#define JIT_PollGC NULL +#endif + #ifndef JIT_MonEnter #define JIT_MonEnter JIT_MonEnter_Portable #endif @@ -1042,7 +1048,6 @@ OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken meta FCDECL2(Object*, JIT_Box_MP_FastPortable, CORINFO_CLASS_HANDLE type, void* data); FCDECL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* data); -FCDECL0(VOID, JIT_PollGC); BOOL ObjIsInstanceOf(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastException = FALSE); BOOL ObjIsInstanceOfCore(Object* pObject, TypeHandle toTypeHnd, BOOL throwCastException = FALSE); diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 56c67fc1e3282..c41ab9e70bdc6 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -281,6 +281,7 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_SpinWait) DllImportEntry(ThreadNative_Interrupt) DllImportEntry(ThreadNative_Sleep) + DllImportEntry(ThreadNative_PollGC) #ifdef FEATURE_COMINTEROP DllImportEntry(ThreadNative_DisableComObjectEagerCleanup) #endif // FEATURE_COMINTEROP