Skip to content

GC Bridge for Android scenarios #114184

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions src/coreclr/clr.featuredefines.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<FeatureCoreCLR>true</FeatureCoreCLR>
<FeaturePerfTracing>true</FeaturePerfTracing>
<ProfilingSupportedBuild>true</ProfilingSupportedBuild>

<!-- !TODO-JAVA! Limit to Android build -->
<FeatureJavaMarshal>true</FeatureJavaMarshal>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetsUnix)' == 'true'">
Expand All @@ -27,6 +30,7 @@
</PropertyGroup>

<PropertyGroup>
<DefineConstants Condition="'$(FeatureJavaMarshal)' == 'true'">$(DefineConstants);FEATURE_JAVAMARSHAL;FEATURE_GCBRIDGE</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
5 changes: 5 additions & 0 deletions src/coreclr/clrdefinitions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ if(FEATURE_OBJCMARSHAL)
add_compile_definitions(FEATURE_OBJCMARSHAL)
endif()

if(FEATURE_JAVAMARSHAL)
add_compile_definitions(FEATURE_JAVAMARSHAL)
add_compile_definitions(FEATURE_GCBRIDGE)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need two different FEATURE_... ifdefs for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

At this point there aren't any. While I was implementing it the logic was clearly distinct though. There is the general concept of the GC Bridge and also the specific Java API and infrastructure that exposes that data. It was natural to separate the two given how we've implemented other interop scenarios involving the GC. It is entirely reasonable to collapse them, but making them distinct now is much easier than retroactively splitting them in the future.

endif()

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

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

# !TODO-JAVA! Limit to Android build
set(FEATURE_JAVAMARSHAL 1)

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 @@ -7705,11 +7705,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 @@ -3344,6 +3344,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_GCBRIDGE)
HNDTYPE_CROSSREFERENCE,
#endif // FEATURE_GCBRIDGE
};

return GetHandleEnumForTypes(types, ARRAY_SIZE(types), ppHandleEnum);
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 TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs);

// 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 TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs)
{
return ::GCToEEInterface::TriggerGCBridge(sccsLen, sccs, ccrsLen, ccrs);
}

void SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
::GCToEEInterface::SyncBlockCacheWeakPtrScan(scanProc, lp1, lp2);
Expand Down
25 changes: 17 additions & 8 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ namespace SVR {
#else // SERVER_GC
namespace WKS {
#endif // SERVER_GC

#include "gcimpl.h"
#include "gcpriv.h"

Expand Down Expand Up @@ -6456,9 +6456,9 @@ class heap_select
if (GCToOSInterface::CanGetCurrentProcessorNumber())
{
uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber();
// For a 32-bit process running on a machine with > 64 procs,
// even though the process can only use up to 32 procs, the processor
// index can be >= 64; or in the cpu group case, if the process is not running in cpu group #0,
// For a 32-bit process running on a machine with > 64 procs,
// even though the process can only use up to 32 procs, the processor
// index can be >= 64; or in the cpu group case, if the process is not running in cpu group #0,
// the GetCurrentProcessorNumber will return a number that's >= 64.
proc_no_to_heap_no[proc_no % MAX_SUPPORTED_CPUS] = (uint16_t)heap_number;
}
Expand All @@ -6482,9 +6482,9 @@ class heap_select
if (GCToOSInterface::CanGetCurrentProcessorNumber())
{
uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber();
// For a 32-bit process running on a machine with > 64 procs,
// even though the process can only use up to 32 procs, the processor
// index can be >= 64; or in the cpu group case, if the process is not running in cpu group #0,
// For a 32-bit process running on a machine with > 64 procs,
// even though the process can only use up to 32 procs, the processor
// index can be >= 64; or in the cpu group case, if the process is not running in cpu group #0,
// the GetCurrentProcessorNumber will return a number that's >= 64.
int adjusted_heap = proc_no_to_heap_no[proc_no % MAX_SUPPORTED_CPUS];
// with dynamic heap count, need to make sure the value is in range.
Expand Down Expand Up @@ -30281,6 +30281,15 @@ void gc_heap::mark_phase (int condemned_gen_number)
drain_mark_queue();
fire_mark_event (ETW::GC_ROOT_HANDLES, current_promoted_bytes, last_promoted_bytes);

#ifdef FEATURE_GCBRIDGE
dprintf(3,("GCBridge to mark handles"));
GCScan::GcScanWithBridge(GCHeap::Promote,
condemned_gen_number, max_generation,
&sc);
drain_mark_queue();
Copy link
Member

Choose a reason for hiding this comment

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

wonder whether drain_mark_queue is required after each scan or can it be combined with the one above ?

// fire_mark_event (ETW::???, current_promoted_bytes, last_promoted_bytes);
Copy link
Member

Choose a reason for hiding this comment

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

assume this will be a TODO to enable later?

#endif // FEATURE_GCBRIDGE

if (!full_p)
{
#ifdef USE_REGIONS
Expand Down Expand Up @@ -42233,7 +42242,7 @@ void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating CARD_
size_t card_last_obj = card_of (last_object_processed);
clear_cards(card, card_last_obj);

// We need to be careful of the accounting here because we could be in the situation where there are more set cards between end of
// We need to be careful of the accounting here because we could be in the situation where there are more set cards between end of
// last set card batch and last_object_processed. We will be clearing all of them. But we can't count the set cards we haven't
// discovered yet or we can get a negative number for n_card_set. However, if last_object_processed lands before what end_card
// corresponds to, we can't count the whole batch because it will be handled by a later clear_cards.
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/gc/gcenv.ee.standalone.inl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ inline bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
return g_theGCToCLR->RefCountedHandleCallbacks(pObject);
}

inline void GCToEEInterface::TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->TriggerGCBridge(sccsLen, sccs, ccrsLen, ccrs);
}

inline void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
assert(g_theGCToCLR != nullptr);
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/gc/gchandletable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ HHANDLETABLE GCHandleStore::GetTable()
// high 16-bits are for the handle info.
int handleInfo = (uint32_t)(ctx->alloc_count) >> 16;

// high 10 bits store the cpu index.
// high 10 bits store the cpu index.
// low 6 bits store the # of handles allocated so far (before the next reset).
int numHandles = handleInfo & 0x3f;
int cpuIndex = handleInfo >> 6;
Expand Down Expand Up @@ -213,7 +213,7 @@ Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE h
HandleType GCHandleManager::HandleFetchType(OBJECTHANDLE handle)
{
uint32_t type = ::HandleFetchType(handle);
assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_INTERIOR_POINTER);
assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_CROSSREFERENCE);
return static_cast<HandleType>(type);
}

Expand Down
11 changes: 7 additions & 4 deletions src/coreclr/gc/gcinterface.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ class IGCToCLREventSink
void FireGCAllocationTick_V1(uint32_t allocationAmount, uint32_t allocationKind) PURE_VIRTUAL

virtual
void FireGCAllocationTick_V4(uint64_t allocationAmount,
uint32_t allocationKind,
uint32_t heapIndex,
void* objectAddress,
void FireGCAllocationTick_V4(uint64_t allocationAmount,
uint32_t allocationKind,
uint32_t heapIndex,
void* objectAddress,
uint64_t objectSize) PURE_VIRTUAL

virtual
Expand Down Expand Up @@ -239,6 +239,9 @@ class IGCToCLR {
virtual
bool RefCountedHandleCallbacks(Object * pObject) PURE_VIRTUAL

virtual
void TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs) PURE_VIRTUAL

// Performs a weak pointer scan of the sync block cache.
virtual
void SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2) PURE_VIRTUAL
Expand Down
22 changes: 21 additions & 1 deletion src/coreclr/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ struct EtwGCSettingsInfo
bool no_affinitize_p;
};

// These definitions are also in managed code.
struct StronglyConnectedComponent
{
size_t Count;
uintptr_t* Context;
};

struct ComponentCrossReference
{
size_t SourceGroupIndex;
size_t DestinationGroupIndex;
};

// Opaque type for tracking object pointers
#ifndef DACCESS_COMPILE
struct OBJECTHANDLE__
Expand Down Expand Up @@ -506,7 +519,14 @@ typedef enum
* have an extra pointer which points at an interior pointer into the first object.
*
*/
HNDTYPE_WEAK_INTERIOR_POINTER = 10
HNDTYPE_WEAK_INTERIOR_POINTER = 10,

/*
* CROSSREFERENCE HANDLES
*
* Crossreference handles are used to track the lifetime of an object in another VM heap.
*/
HNDTYPE_CROSSREFERENCE = 11
} HandleType;

typedef enum
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/gc/gcscan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ void GCScan::GcScanHandles (promote_func* fn, int condemned, int max_gen,
}
}

#ifdef FEATURE_GCBRIDGE
void GCScan::GcScanWithBridge (promote_func* fn, int condemned,
int max_gen, ScanContext* sc)
{
STRESS_LOG0(LF_GC|LF_GCROOTS, LL_INFO10, "GcScanWithBridge\n");
_ASSERTE(sc->promotion);
_ASSERTE(!sc->concurrent);
Ref_TraceGCBridge(condemned, max_gen, sc, fn);
}
#endif // FEATURE_GCBRIDGE

/*
* Scan all handle roots in this 'namespace' for profiling
*/
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/gc/gcscan.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class GCScan
//
static void GcScanHandles (promote_func* fn, int condemned, int max_gen, ScanContext* sc);

#ifdef FEATURE_GCBRIDGE
static void GcScanWithBridge (promote_func* fn, int condemned, int max_gen, ScanContext* sc);
#endif // FEATURE_GCBRIDGE

static void GcRuntimeStructuresValid (BOOL bValid);

static bool GetGcRuntimeStructuresValid ();
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/gc/handletableconstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#endif

#define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10
#define HANDLE_MAX_INTERNAL_TYPES 12
#define HANDLE_MAX_INTERNAL_TYPES 16
Copy link
Member

Choose a reason for hiding this comment

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

Are there perf consequences from bumping this limit? Should we start recycling deprecated HDNTYPE values (e.g. HNDTYPE_ASYNCPINNED)?

Copy link
Member

Choose a reason for hiding this comment

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

just need to be careful that recycling wouldnt cause backcompat issues for standalone gc.


/*--------------------------------------------------------------------------*/

Expand Down
Loading
Loading