Skip to content

Add CoreCLR support for android GC bridge #116310

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

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8e0dd9c
Add support for the new cross reference GCHandle type
BrzVlad Apr 17, 2025
0d9227a
Add interop glue for bridge
BrzVlad Apr 17, 2025
d3a3a72
Add GCBridge test
BrzVlad Apr 18, 2025
bb1428d
Implement scanning of bridge objects and construction of SCCs
BrzVlad Apr 18, 2025
d2033e7
Add tarjan bridge computation
BrzVlad Apr 28, 2025
89b4808
Fix object validation
BrzVlad Apr 30, 2025
38f01cd
bgc and multiple heap support for bridge obj processing
BrzVlad May 2, 2025
860aaa1
Add incorrect waiting for gc bridge to finish
BrzVlad May 16, 2025
305ebe6
Update code to new API
BrzVlad May 19, 2025
8c6a3e9
Clear weakrefs for bridge objects with collected java peers
BrzVlad May 19, 2025
ac1628c
Wait for bridge processing while resolving the gchandle to prevent races
BrzVlad May 20, 2025
160e663
Maoni review
BrzVlad May 22, 2025
b8e12b9
Aaron review
BrzVlad Jun 3, 2025
29074d4
Update to new API
BrzVlad Jun 4, 2025
bbd813b
Fix build
BrzVlad Jun 5, 2025
4d61fbe
Use cross refs args directly in more places
BrzVlad Jun 13, 2025
ee7dcb5
Change unreachable handle length from int to size_t
BrzVlad Jun 14, 2025
3806737
Remove FEATURE_GCBRIDGE
BrzVlad Jun 14, 2025
090e55f
Move interop javamarshal code out of interop shared file
BrzVlad Jun 14, 2025
c6ec8f9
Use asserts in test
BrzVlad Jun 14, 2025
59d6b35
Add bridge waiting to WeakReference<T> as well
BrzVlad Jun 14, 2025
a3e864d
Review
BrzVlad Jun 16, 2025
0276046
review
BrzVlad Jun 19, 2025
25edbb7
High precision timestamp
BrzVlad Jun 19, 2025
7b63046
Naming consistency in gcbridge.cpp, matching rest of the runtime
BrzVlad Jun 19, 2025
28a2b21
Fix interface breakage
BrzVlad Jun 19, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ private static void InternalFreeWithGCTransition(IntPtr dependentHandle)
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCHandle_InternalFreeWithGCTransition")]
private static partial void _InternalFreeWithGCTransition(IntPtr dependentHandle);

#if FEATURE_JAVAMARSHAL
internal static object? InternalGetBridgeWait(IntPtr handle)
{
object? target = null;

if (GCHandle.InternalTryGetBridgeWait(handle, ref target))
return target;

InternalGetBridgeWait(handle, ObjectHandleOnStack.Create(ref target));

return target;
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool InternalTryGetBridgeWait(IntPtr handle, ref object? result);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCHandle_InternalGetBridgeWait")]
private static partial void InternalGetBridgeWait(IntPtr handle, ObjectHandleOnStack result);

#endif
#if DEBUG
// The runtime performs additional checks in debug builds
[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/clr.featuredefines.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
<ProfilingSupportedBuild>true</ProfilingSupportedBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetsAndroid)' == 'true' OR '$(Configuration)' == 'debug' OR '$(Configuration)' == 'checked'">
<FeatureJavaMarshal>true</FeatureJavaMarshal>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetsUnix)' == 'true'">
<FeatureXplatEventSource Condition="'$(FeatureXplatEventSource)' == '' AND '$(TargetOS)' == 'linux'">true</FeatureXplatEventSource>
<FeatureComWrappers>true</FeatureComWrappers>
Expand All @@ -28,6 +32,7 @@
</PropertyGroup>

<PropertyGroup>
<DefineConstants Condition="'$(FeatureJavaMarshal)' == 'true'">$(DefineConstants);FEATURE_JAVAMARSHAL</DefineConstants>
<DefineConstants Condition="'$(FeatureComWrappers)' == 'true'">$(DefineConstants);FEATURE_COMWRAPPERS</DefineConstants>
<DefineConstants Condition="'$(FeatureCominterop)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP</DefineConstants>
<DefineConstants Condition="'$(FeatureCominteropApartmentSupport)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT</DefineConstants>
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/clrdefinitions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ endif()

# add_compile_definitions(FEATURE_RUNTIME_ASYNC)

add_compile_definitions($<${FEATURE_JAVAMARSHAL}:FEATURE_JAVAMARSHAL>)

add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>>:FEATURE_PROFAPI_ATTACH_DETACH>)

add_definitions(-DFEATURE_READYTORUN)
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/clrfeatures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ if (CLR_CMAKE_TARGET_APPLE)
set(FEATURE_OBJCMARSHAL 1)
endif()

if(NOT DEFINED FEATURE_JAVAMARSHAL)
if(CLR_CMAKE_TARGET_ANDROID)
set(FEATURE_JAVAMARSHAL 1)
else()
set(FEATURE_JAVAMARSHAL $<IF:$<CONFIG:Debug,Checked>,1,0>)
endif()
endif()

if (CLR_CMAKE_TARGET_WIN32)
set(FEATURE_TYPEEQUIVALENCE 1)
endif(CLR_CMAKE_TARGET_WIN32)
Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7700,11 +7700,18 @@ void CALLBACK DacHandleWalker::EnumCallback(PTR_UNCHECKED_OBJECTREF handle, uint
data.Handle = TO_CDADDR(handle.GetAddr());
data.Type = param->Type;
if (param->Type == HNDTYPE_DEPENDENT)
{
data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr();
else if (param->Type == HNDTYPE_WEAK_INTERIOR_POINTER)
}
else if (param->Type == HNDTYPE_WEAK_INTERIOR_POINTER
|| param->Type == HNDTYPE_CROSSREFERENCE)
{
data.Secondary = TO_CDADDR(HndGetHandleExtraInfo(handle.GetAddr()));
}
else
{
data.Secondary = 0;
}
data.AppDomain = param->AppDomain;
GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &data.RefCount, &data.JupiterRefCount, &data.IsPegged, &data.StrongReference);
data.StrongReference |= (BOOL)IsAlwaysStrongReference(param->Type);
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3338,6 +3338,9 @@ HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum)
#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
HNDTYPE_REFCOUNTED,
#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
#if defined(FEATURE_JAVAMARSHAL)
HNDTYPE_CROSSREFERENCE,
#endif // FEATURE_JAVAMARSHAL
};

return GetHandleEnumForTypes(types, ARRAY_SIZE(types), ppHandleEnum);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/gc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(GC_SOURCES
gceesvr.cpp
gceewks.cpp
gcload.cpp
gcbridge.cpp
handletablecache.cpp)

if(CLR_CMAKE_HOST_UNIX)
Expand Down Expand Up @@ -55,6 +56,7 @@ if (CLR_CMAKE_TARGET_WIN32)
env/volatile.h
gc.h
gcconfig.h
gcbridge.h
gcdesc.h
gcenv.ee.standalone.inl
gcenv.inl
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/gc/env/gcenv.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class GCToEEInterface
// Promote refcounted handle callback
static bool RefCountedHandleCallbacks(Object * pObject);

static void TriggerClientBridgeProcessing(MarkCrossReferencesArgs* args);

// Sync block cache management
static void SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2);
static void SyncBlockCacheDemote(int max_gen);
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/gc/env/gctoeeinterface.standalone.inl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ namespace standalone
return ::GCToEEInterface::RefCountedHandleCallbacks(pObject);
}

void TriggerClientBridgeProcessing(MarkCrossReferencesArgs* args)
{
return ::GCToEEInterface::TriggerClientBridgeProcessing(args);
}

void SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
::GCToEEInterface::SyncBlockCacheWeakPtrScan(scanProc, lp1, lp2);
Expand Down
95 changes: 93 additions & 2 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,8 @@ enum gc_join_stage
// No longer in use but do not remove, see comments for this enum.
gc_join_disable_software_write_watch = 38,
gc_join_merge_temp_fl = 39,
gc_join_max = 40
gc_join_bridge_processing = 40,
gc_join_max = 41
};

enum gc_join_flavor
Expand Down Expand Up @@ -2865,6 +2866,11 @@ FinalizerWorkItem* gc_heap::finalizer_work;
BOOL gc_heap::proceed_with_gc_p = FALSE;
GCSpinLock gc_heap::gc_lock;

#ifdef FEATURE_JAVAMARSHAL
uint8_t** gc_heap::global_bridge_list;
size_t gc_heap::num_global_bridge_objs;
#endif //FEATURE_JAVAMARSHAL

#ifdef BACKGROUND_GC
uint64_t gc_heap::total_uoh_a_last_bgc = 0;
#endif //BACKGROUND_GC
Expand Down Expand Up @@ -30449,6 +30455,41 @@ void gc_heap::mark_phase (int condemned_gen_number)
mark_queue.verify_empty();
fire_mark_event (ETW::GC_ROOT_DH_HANDLES, current_promoted_bytes, last_promoted_bytes);

#ifdef FEATURE_JAVAMARSHAL

#ifdef MULTIPLE_HEAPS
dprintf(3, ("Joining for short weak handle scan"));
gc_t_join.join(this, gc_join_bridge_processing);
if (gc_t_join.joined())
{
#endif //MULTIPLE_HEAPS
global_bridge_list = GCScan::GcProcessBridgeObjects (condemned_gen_number, max_generation, &sc, &num_global_bridge_objs);

#ifdef MULTIPLE_HEAPS
dprintf (3, ("Starting all gc thread after bridge processing"));
gc_t_join.restart();
}
#endif //MULTIPLE_HEAPS

{
int thread = heap_number;
// Each thread will receive an equal chunk of bridge objects, with the last thread
// handling a few more objects from the remainder.
size_t count_per_heap = num_global_bridge_objs / n_heaps;
size_t start_index = thread * count_per_heap;
size_t end_index = (thread == n_heaps - 1) ? num_global_bridge_objs : (thread + 1) * count_per_heap;

for (size_t obj_idx = start_index; obj_idx < end_index; obj_idx++)
{
mark_object_simple (&global_bridge_list[obj_idx] THREAD_NUMBER_ARG);
}

drain_mark_queue();
// using GC_ROOT_DH_HANDLES temporarily. add a new value for GC_ROOT_BRIDGE
fire_mark_event (ETW::GC_ROOT_DH_HANDLES, current_promoted_bytes, last_promoted_bytes);
}
#endif //FEATURE_JAVAMARSHAL

#ifdef MULTIPLE_HEAPS
dprintf(3, ("Joining for short weak handle scan"));
gc_t_join.join(this, gc_join_null_dead_short_weak);
Expand Down Expand Up @@ -39180,6 +39221,41 @@ void gc_heap::background_mark_phase ()
dprintf (2, ("after NR 1st Hov count: %zu", bgc_overflow_count));
bgc_overflow_count = 0;

#ifdef FEATURE_JAVAMARSHAL

// FIXME Any reason this code should be different for BGC ? Otherwise extract it to some common method ?

#ifdef MULTIPLE_HEAPS
dprintf(3, ("Joining for short weak handle scan"));
gc_t_join.join(this, gc_join_bridge_processing);
if (gc_t_join.joined())
{
#endif //MULTIPLE_HEAPS
global_bridge_list = GCScan::GcProcessBridgeObjects (max_generation, max_generation, &sc, &num_global_bridge_objs);

#ifdef MULTIPLE_HEAPS
dprintf (3, ("Starting all gc thread after bridge processing"));
gc_t_join.restart();
}
#endif //MULTIPLE_HEAPS

{
int thread = heap_number;
// Each thread will receive an equal chunk of bridge objects, with the last thread
// handling a few more objects from the remainder.
size_t count_per_heap = num_global_bridge_objs / n_heaps;
size_t start_index = thread * count_per_heap;
size_t end_index = (thread == n_heaps - 1) ? num_global_bridge_objs : (thread + 1) * count_per_heap;

for (size_t obj_idx = start_index; obj_idx < end_index; obj_idx++)
{
mark_object_simple (&global_bridge_list[obj_idx] THREAD_NUMBER_ARG);
}

drain_mark_queue();
}
#endif //FEATURE_JAVAMARSHAL

#ifdef MULTIPLE_HEAPS
bgc_t_join.join(this, gc_join_null_dead_short_weak);
if (bgc_t_join.joined())
Expand Down Expand Up @@ -49709,6 +49785,11 @@ HRESULT GCHeap::Initialize()
////
// GC callback functions
bool GCHeap::IsPromoted(Object* object)
{
return IsPromoted(object, true);
}

bool GCHeap::IsPromoted(Object* object, bool bVerifyNextHeader)
{
uint8_t* o = (uint8_t*)object;

Expand Down Expand Up @@ -49746,10 +49827,11 @@ bool GCHeap::IsPromoted(Object* object)
#endif //USE_REGIONS
}

// Walking refs when objects are marked seems unexpected
#ifdef _DEBUG
if (o)
{
((CObjectHeader*)o)->Validate(TRUE, TRUE, is_marked);
((CObjectHeader*)o)->Validate(TRUE, bVerifyNextHeader, is_marked);

// Frozen objects aren't expected to be "not promoted" here
assert(is_marked || !IsInFrozenSegment(object));
Expand Down Expand Up @@ -53227,6 +53309,15 @@ void GCHeap::DiagGetGCSettings(EtwGCSettingsInfo* etw_settings)
#endif //FEATURE_EVENT_TRACE
}

void GCHeap::NullBridgeObjectsWeakRefs(size_t length, void* unreachableObjectHandles)
{
#ifdef FEATURE_JAVAMARSHAL
Ref_NullBridgeObjectsWeakRefs(length, unreachableObjectHandles);
#else
assert(false);
#endif
}

#if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
// This code is designed to catch the failure to update the write barrier
// The way it works is to copy the whole heap right after every GC. The write
Expand Down
Loading
Loading