Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NativeAOT] Allow reverse pinvoke in DoNotTriggerGc thread state regardless of coop mode. #85435

Merged
merged 6 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/coreclr/nativeaot/Runtime/EHHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,10 @@ EXTERN_C int32_t __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExc

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();

Expand Down
30 changes: 15 additions & 15 deletions src/coreclr/nativeaot/Runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1125,34 +1125,33 @@ EXTERN_C NATIVEAOT_API uint32_t __cdecl RhCompatibleReentrantWaitAny(UInt32_BOOL

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())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to do this before the TSF_Attached check. We do not want to attach untached GC threads as a side-effect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, on server GC this could be unattached thread.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to pass ServerGarbageCollection=true to src\tests\build.cmd ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can set DOTNET_gcServer=1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out src\tests\build.cmd can pass through paramters to msbuild, but src/tests/build.proj was not propagating some parameters that could be useful.

{
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());
jkotas marked this conversation as resolved.
Show resolved Hide resolved
// 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);

Expand Down Expand Up @@ -1233,6 +1232,7 @@ FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame

FORCEINLINE void Thread::InlinePInvoke(PInvokeTransitionFrame * pFrame)
{
ASSERT(!IsDoNotTriggerGcSet() || ThreadStore::IsTrapThreadsRequested());
pFrame->m_pThread = this;
// set our mode to preemptive
VolatileStoreWithoutBarrier(&m_pTransitionFrame, pFrame);
Expand Down
1 change: 1 addition & 0 deletions src/tests/build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@
<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>
Expand Down