Skip to content

Commit 1e7433e

Browse files
authored
Implement GC bridge VM support in NativeAOT (#117738)
1 parent 6783779 commit 1e7433e

File tree

17 files changed

+372
-36
lines changed

17 files changed

+372
-36
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@
223223
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Marshal.CoreCLR.cs" />
224224
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\MemoryMarshal.CoreCLR.cs" />
225225
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NativeLibrary.CoreCLR.cs" />
226+
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Java\JavaMarshal.CoreCLR.cs" Condition="'$(FeatureJavaMarshal)' == 'true'" />
226227
<Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\X86Base.CoreCLR.cs" />
227228
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.CoreCLR.cs" />
228229
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.cs renamed to src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,24 @@ public static partial class JavaMarshal
1212
{
1313
public static unsafe void Initialize(delegate* unmanaged<MarkCrossReferencesArgs*, void> markCrossReferences)
1414
{
15-
#if NATIVEAOT
16-
throw new NotImplementedException();
17-
#elif FEATURE_JAVAMARSHAL
1815
ArgumentNullException.ThrowIfNull(markCrossReferences);
1916

2017
if (!InitializeInternal((IntPtr)markCrossReferences))
2118
{
2219
throw new InvalidOperationException(SR.InvalidOperation_ReinitializeJavaMarshal);
2320
}
24-
#else
25-
throw new PlatformNotSupportedException();
26-
#endif
2721
}
2822

2923
public static unsafe GCHandle CreateReferenceTrackingHandle(object obj, void* context)
3024
{
31-
#if NATIVEAOT
32-
throw new NotImplementedException();
33-
#elif FEATURE_JAVAMARSHAL
3425
ArgumentNullException.ThrowIfNull(obj);
3526

3627
IntPtr handle = CreateReferenceTrackingHandleInternal(ObjectHandleOnStack.Create(ref obj), context);
3728
return GCHandle.FromIntPtr(handle);
38-
#else
39-
throw new PlatformNotSupportedException();
40-
#endif
4129
}
4230

4331
public static unsafe void* GetContext(GCHandle obj)
4432
{
45-
#if NATIVEAOT
46-
throw new NotImplementedException();
47-
#elif FEATURE_JAVAMARSHAL
4833
IntPtr handle = GCHandle.ToIntPtr(obj);
4934
if (handle == IntPtr.Zero
5035
|| !GetContextInternal(handle, out void* context))
@@ -53,31 +38,21 @@ public static unsafe GCHandle CreateReferenceTrackingHandle(object obj, void* co
5338
}
5439

5540
return context;
56-
#else
57-
throw new PlatformNotSupportedException();
58-
#endif
5941
}
6042

6143
public static unsafe void FinishCrossReferenceProcessing(
6244
MarkCrossReferencesArgs* crossReferences,
6345
ReadOnlySpan<GCHandle> unreachableObjectHandles)
6446
{
65-
#if NATIVEAOT
66-
throw new NotImplementedException();
67-
#elif FEATURE_JAVAMARSHAL
6847
fixed (GCHandle* pHandles = unreachableObjectHandles)
6948
{
7049
FinishCrossReferenceProcessing(
7150
crossReferences,
7251
(nuint)unreachableObjectHandles.Length,
7352
pHandles);
7453
}
75-
#else
76-
throw new PlatformNotSupportedException();
77-
#endif
7854
}
7955

80-
#if FEATURE_JAVAMARSHAL && !NATIVEAOT
8156
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "JavaMarshal_Initialize")]
8257
[return: MarshalAs(UnmanagedType.Bool)]
8358
private static partial bool InitializeInternal(IntPtr callback);
@@ -92,6 +67,5 @@ public static unsafe void FinishCrossReferenceProcessing(
9267
[SuppressGCTransition]
9368
[return: MarshalAs(UnmanagedType.Bool)]
9469
private static unsafe partial bool GetContextInternal(IntPtr handle, out void* context);
95-
#endif
9670
}
9771
}

src/coreclr/nativeaot/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ endif (CLR_CMAKE_HOST_UNIX)
3030

3131
if(CLR_CMAKE_TARGET_ANDROID)
3232
add_definitions(-DFEATURE_EMULATED_TLS)
33+
set(FEATURE_JAVAMARSHAL 1)
3334
endif()
3435

36+
if(NOT DEFINED FEATURE_JAVAMARSHAL)
37+
set(FEATURE_JAVAMARSHAL $<IF:$<CONFIG:Debug,Checked>,1,0>)
38+
endif()
39+
40+
add_compile_definitions($<${FEATURE_JAVAMARSHAL}:FEATURE_JAVAMARSHAL>)
41+
3542
add_subdirectory(Bootstrap)
3643
add_subdirectory(Runtime)

src/coreclr/nativeaot/Runtime/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(COMMON_RUNTIME_SOURCES
2020
gcenv.ee.cpp
2121
GcStressControl.cpp
2222
HandleTableHelpers.cpp
23+
interoplibinterface_java.cpp
2324
MathHelpers.cpp
2425
MiscHelpers.cpp
2526
TypeManager.cpp
@@ -38,6 +39,7 @@ set(COMMON_RUNTIME_SOURCES
3839
yieldprocessornormalized.cpp
3940

4041
${GC_DIR}/gceventstatus.cpp
42+
${GC_DIR}/gcbridge.cpp
4143
${GC_DIR}/gcload.cpp
4244
${GC_DIR}/gcconfig.cpp
4345
${GC_DIR}/gchandletable.cpp

src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "objecthandle.h"
77
#include "RestrictedCallouts.h"
88
#include "gchandleutilities.h"
9+
#include "interoplibinterface.h"
910

1011

1112
FCIMPL2(OBJECTHANDLE, RhpHandleAlloc, Object *pObject, int type)
@@ -64,6 +65,27 @@ FCIMPL2(void, RhUnregisterRefCountedHandleCallback, void * pCallout, MethodTable
6465
}
6566
FCIMPLEND
6667

68+
FCIMPL2(OBJECTHANDLE, RhpHandleAllocCrossReference, Object *pPrimary, void *pContext)
69+
{
70+
return GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateHandleWithExtraInfo(pPrimary, HNDTYPE_CROSSREFERENCE, pContext);
71+
}
72+
FCIMPLEND
73+
74+
FCIMPL2(FC_BOOL_RET, RhHandleTryGetCrossReferenceContext, OBJECTHANDLE handle, void **pContext)
75+
{
76+
*pContext = nullptr;
77+
78+
IGCHandleManager* gcHandleManager = GCHandleUtilities::GetGCHandleManager();
79+
if (gcHandleManager->HandleFetchType(handle) != HNDTYPE_CROSSREFERENCE)
80+
{
81+
FC_RETURN_BOOL(false);
82+
}
83+
84+
*pContext = gcHandleManager->GetExtraInfoFromHandle(handle);
85+
FC_RETURN_BOOL(true);
86+
}
87+
FCIMPLEND
88+
6789
// This structure mirrors the managed type System.Runtime.InteropServices.ComWrappers.ManagedObjectWrapper.
6890
struct ManagedObjectWrapper
6991
{

src/coreclr/nativeaot/Runtime/gcenv.ee.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,4 +809,11 @@ void GCToEEInterface::FreeStringConfigValue(const char* value)
809809
delete[] value;
810810
}
811811

812+
void GCToEEInterface::TriggerClientBridgeProcessing(MarkCrossReferencesArgs* args)
813+
{
814+
#ifdef FEATURE_JAVAMARSHAL
815+
JavaMarshalNative::TriggerClientBridgeProcessing(args);
816+
#endif
817+
}
818+
812819
#endif // !DACCESS_COMPILE

src/coreclr/nativeaot/Runtime/interoplibinterface.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,15 @@ class ObjCMarshalNative
2222
};
2323

2424
#endif // FEATURE_OBJCMARSHAL
25+
26+
#ifdef FEATURE_JAVAMARSHAL
27+
28+
struct MarkCrossReferencesArgs;
29+
30+
class JavaMarshalNative
31+
{
32+
public:
33+
static void TriggerClientBridgeProcessing(
34+
MarkCrossReferencesArgs* args);
35+
};
36+
#endif // FEATURE_JAVAMARSHAL
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#ifdef FEATURE_JAVAMARSHAL
5+
6+
// Runtime headers
7+
#include "common.h"
8+
#include "gcenv.h"
9+
#include "gcenv.ee.h"
10+
#include "gcheaputilities.h"
11+
#include "gchandleutilities.h"
12+
#include "thread.h"
13+
#include "threadstore.h"
14+
#include "threadstore.inl"
15+
#include "event.h"
16+
17+
#include "interoplibinterface.h"
18+
19+
using CrossreferenceHandleCallback = void(__stdcall *)(MarkCrossReferencesArgs*);
20+
21+
namespace
22+
{
23+
volatile CrossreferenceHandleCallback g_MarkCrossReferences = NULL;
24+
25+
Volatile<bool> g_GCBridgeActive = false;
26+
CLREventStatic g_bridgeFinished;
27+
28+
void ReleaseGCBridgeArgumentsWorker(
29+
MarkCrossReferencesArgs* args)
30+
{
31+
_ASSERTE(args != NULL);
32+
33+
// Memory was allocated for the collections by the GC.
34+
// See callers of GCToEEInterface::TriggerGCBridge().
35+
36+
// Free memory in each of the SCCs
37+
for (size_t i = 0; i < args->ComponentCount; i++)
38+
{
39+
delete[] args->Components[i].Contexts;
40+
}
41+
delete[] args->Components;
42+
delete[] args->CrossReferences;
43+
delete args;
44+
}
45+
}
46+
47+
void JavaMarshalNative::TriggerClientBridgeProcessing(
48+
_In_ MarkCrossReferencesArgs* args)
49+
{
50+
_ASSERTE(GCHeapUtilities::IsGCInProgress());
51+
52+
if (g_GCBridgeActive)
53+
{
54+
// Release the memory allocated since the GCBridge
55+
// is already running and we're not passing them to it.
56+
ReleaseGCBridgeArgumentsWorker(args);
57+
return;
58+
}
59+
60+
// Not initialized
61+
if (g_MarkCrossReferences == NULL)
62+
{
63+
// Release the memory allocated since we
64+
// don't have a GC bridge callback.
65+
ReleaseGCBridgeArgumentsWorker(args);
66+
return;
67+
}
68+
69+
g_MarkCrossReferences(args);
70+
71+
// This runs during GC while the world is stopped, no synchronisation required
72+
g_bridgeFinished.Reset();
73+
74+
// Mark the GCBridge as active.
75+
g_GCBridgeActive = true;
76+
}
77+
78+
extern "C" BOOL QCALLTYPE JavaMarshal_Initialize(
79+
void* markCrossReferences)
80+
{
81+
_ASSERTE(markCrossReferences != NULL);
82+
83+
BOOL success = FALSE;
84+
85+
// Switch to Cooperative mode since we are setting callbacks that
86+
// will be used during a GC and we want to ensure a GC isn't occurring
87+
// while they are being set.
88+
Thread* pThisThread = ThreadStore::GetCurrentThreadIfAvailable();
89+
pThisThread->DeferTransitionFrame();
90+
pThisThread->DisablePreemptiveMode();
91+
92+
if (PalInterlockedCompareExchangePointer((void* volatile*)&g_MarkCrossReferences, (void*)markCrossReferences, NULL) == NULL)
93+
{
94+
success = g_bridgeFinished.CreateManualEventNoThrow(false);
95+
}
96+
97+
pThisThread->EnablePreemptiveMode();
98+
99+
return success;
100+
}
101+
102+
extern "C" void QCALLTYPE JavaMarshal_FinishCrossReferenceProcessing(
103+
MarkCrossReferencesArgs *crossReferences,
104+
size_t length,
105+
void* unreachableObjectHandles)
106+
{
107+
_ASSERTE(crossReferences->ComponentCount >= 0);
108+
109+
_ASSERTE(g_GCBridgeActive);
110+
111+
// Mark the GCBridge as inactive.
112+
// This must be synchronized with the GC so switch to cooperative mode.
113+
{
114+
Thread* pThisThread = ThreadStore::GetCurrentThreadIfAvailable();
115+
pThisThread->DeferTransitionFrame();
116+
pThisThread->DisablePreemptiveMode();
117+
118+
GCHeapUtilities::GetGCHeap()->NullBridgeObjectsWeakRefs(length, unreachableObjectHandles);
119+
120+
IGCHandleManager* pHandleManager = GCHandleUtilities::GetGCHandleManager();
121+
OBJECTHANDLE* handles = (OBJECTHANDLE*)unreachableObjectHandles;
122+
for (size_t i = 0; i < length; i++)
123+
pHandleManager->DestroyHandleOfUnknownType(handles[i]);
124+
125+
g_GCBridgeActive = false;
126+
g_bridgeFinished.Set();
127+
128+
pThisThread->EnablePreemptiveMode();
129+
}
130+
131+
ReleaseGCBridgeArgumentsWorker(crossReferences);
132+
}
133+
134+
FCIMPL2(FC_BOOL_RET, GCHandle_InternalTryGetBridgeWait, OBJECTHANDLE handle, OBJECTREF* pObjResult)
135+
{
136+
if (g_GCBridgeActive)
137+
{
138+
FC_RETURN_BOOL(false);
139+
}
140+
141+
*pObjResult = ObjectFromHandle(handle);
142+
FC_RETURN_BOOL(true);
143+
}
144+
FCIMPLEND
145+
146+
extern "C" void QCALLTYPE GCHandle_InternalGetBridgeWait(OBJECTHANDLE handle, OBJECTREF* pObj)
147+
{
148+
_ASSERTE(pObj != NULL);
149+
150+
// Transition to cooperative mode to ensure that the GC is not in progress
151+
Thread* pThisThread = ThreadStore::GetCurrentThreadIfAvailable();
152+
pThisThread->DeferTransitionFrame();
153+
pThisThread->DisablePreemptiveMode();
154+
155+
while (g_GCBridgeActive)
156+
{
157+
// This wait will transition to pre-emptive mode to wait for the bridge to finish.
158+
g_bridgeFinished.Wait(INFINITE, false, false);
159+
}
160+
161+
// If we reach here, then the bridge has finished processing and we can be sure that
162+
// it isn't currently active.
163+
164+
// No GC can happen between the wait and obtaining of the reference, so the
165+
// bridge processing status can't change, guaranteeing the nulling of weak refs
166+
// took place in the bridge processing finish stage.
167+
*pObj = ObjectFromHandle(handle);
168+
169+
// Re-enable preemptive mode before we exit the QCall to ensure we're in the right GC state.
170+
pThisThread->EnablePreemptiveMode();
171+
}
172+
173+
#endif // FEATURE_JAVAMARSHAL

src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
<Compile Include="System\CrashInfo.cs" />
160160
<Compile Include="System\Runtime\InteropServices\ComAwareWeakReference.NativeAot.cs" />
161161
<Compile Include="System\Runtime\InteropServices\CriticalHandle.NativeAot.cs" />
162+
<Compile Include="System\Runtime\InteropServices\Java\JavaMarshal.NativeAot.cs" Condition="'$(FeatureJavaMarshal)' == 'true'" />
162163
<Compile Include="System\Activator.NativeAot.cs" />
163164
<Compile Include="System\AppContext.NativeAot.cs" />
164165
<Compile Include="System\ArgIterator.cs" />

0 commit comments

Comments
 (0)