Thread * pThread = ThreadStore::GetCurrentThread();
- // If the thread is currently in the "do not trigger GC" mode, we must not allocate, we must not reverse pinvoke, or
- // return from a pinvoke. All of these things will deadlock with the GC and they all become increasingly likely as
- // exception dispatch kicks off. So we just address this as early as possible with a FailFast. The most
- // likely case where this occurs is in our GC-callouts for Jupiter lifetime management -- in that case, we have
- // managed code that calls to native code (without pinvoking) which might have a bug that causes an AV.
+ // A thread in DoNotTriggerGc mode has many restrictions that will become increasingly likely to be violated as
+ // exception dispatch kicks off. So we just address this as early as possible with a FailFast.
+ // The most likely case where this occurs is in GC-callouts -- in that case, we have
+ // managed code that runs on behalf of GC, which might have a bug that causes an AV.
if (pThread->IsDoNotTriggerGcSet())
RhFailFast();
FORCEINLINE bool Thread::InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame)
{
- // Do we need to attach the thread?
- if (!IsStateSet(TSF_Attached))
- return false; // thread is not attached
+ // remember the current transition frame, so it will be restored when we return from reverse pinvoke
+ pFrame->m_savedPInvokeTransitionFrame = m_pTransitionFrame;
// If the thread is already in cooperative mode, this is a bad transition that will be a fail fast unless we are in
// a do not trigger mode. The exception to the rule allows us to have [UnmanagedCallersOnly] methods that are called via
// the "restricted GC callouts" as well as from native, which is necessary because the methods are CCW vtable
// methods on interfaces passed to native.
- if (IsCurrentThreadInCooperativeMode())
+ // We will allow threads in DoNotTriggerGc mode to do reverse PInvoke regardless of their coop state.
+ if (IsDoNotTriggerGcSet())
{
- if (IsDoNotTriggerGcSet())
- {
- // RhpTrapThreads will always be set in this case, so we must skip that check. We must be sure to
- // zero-out our 'previous transition frame' state first, however.
- pFrame->m_savedPInvokeTransitionFrame = NULL;
- return true;
- }
+ // We expect this scenario only when EE is stopped.
+ ASSERT(ThreadStore::IsTrapThreadsRequested());
+ // no need to do anything
+ return true;
+ }
+ // Do we need to attach the thread?
+ if (!IsStateSet(TSF_Attached))
+ return false; // thread is not attached
+
+ if (IsCurrentThreadInCooperativeMode())
return false; // bad transition
- }
// this is an ordinary transition to managed code
// GC threads should not do that
ASSERT(!IsGCSpecial());
- // save the previous transition frame
- pFrame->m_savedPInvokeTransitionFrame = m_pTransitionFrame;
-
// must be in cooperative mode when checking the trap flag
VolatileStoreWithoutBarrier(&m_pTransitionFrame, NULL);
FORCEINLINE void Thread::InlinePInvoke(PInvokeTransitionFrame * pFrame)
{
+ ASSERT(!IsDoNotTriggerGcSet() || ThreadStore::IsTrapThreadsRequested());
pFrame->m_pThread = this;
// set our mode to preemptive
VolatileStoreWithoutBarrier(&m_pTransitionFrame, pFrame);
<GroupBuildCmd>$(GroupBuildCmd) "/p:PackageOS=$(PackageOS)"</GroupBuildCmd>
<GroupBuildCmd>$(GroupBuildCmd) "/p:RuntimeFlavor=$(RuntimeFlavor)"</GroupBuildCmd>
<GroupBuildCmd>$(GroupBuildCmd) "/p:RuntimeVariant=$(RuntimeVariant)"</GroupBuildCmd>
+ <GroupBuildCmd Condition="'$(ServerGarbageCollection)' != ''">$(GroupBuildCmd) "/p:ServerGarbageCollection=$(ServerGarbageCollection)"</GroupBuildCmd>
<GroupBuildCmd>$(GroupBuildCmd) "/p:CLRTestBuildAllTargets=$(CLRTestBuildAllTargets)"</GroupBuildCmd>
<GroupBuildCmd>$(GroupBuildCmd) "/p:UseCodeFlowEnforcement=$(UseCodeFlowEnforcement)"</GroupBuildCmd>
<GroupBuildCmd>$(GroupBuildCmd) "/p:__TestGroupToBuild=$(__TestGroupToBuild)"</GroupBuildCmd>