diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 17ed60b5f8deca..0530e2aa9db3b6 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -3,6 +3,9 @@ true true true + + + true @@ -27,6 +30,7 @@ + $(DefineConstants);FEATURE_JAVAMARSHAL;FEATURE_GCBRIDGE $(DefineConstants);FEATURE_COMWRAPPERS $(DefineConstants);FEATURE_COMINTEROP $(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 550ce70a14ef7a..1eeec1f22fb91c 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -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) +endif() + add_compile_definitions($<$>>:FEATURE_PROFAPI_ATTACH_DETACH>) add_definitions(-DFEATURE_READYTORUN) diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 6bb761a8e0e2f8..20e9219fa7a2dc 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -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) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 17a327dba01d1e..b8fd0d067383e3 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -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); diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 06b00e4ffa75f2..7098ed73e99a6e 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -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); diff --git a/src/coreclr/gc/env/gcenv.ee.h b/src/coreclr/gc/env/gcenv.ee.h index 865eb2902a8798..5e06b8734df254 100644 --- a/src/coreclr/gc/env/gcenv.ee.h +++ b/src/coreclr/gc/env/gcenv.ee.h @@ -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); diff --git a/src/coreclr/gc/env/gctoeeinterface.standalone.inl b/src/coreclr/gc/env/gctoeeinterface.standalone.inl index 429d4f4c0248f6..494965c271f25a 100644 --- a/src/coreclr/gc/env/gctoeeinterface.standalone.inl +++ b/src/coreclr/gc/env/gctoeeinterface.standalone.inl @@ -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); diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 255e9d1199ad9e..b40433a676ed9f 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -51,7 +51,7 @@ namespace SVR { #else // SERVER_GC namespace WKS { #endif // SERVER_GC - + #include "gcimpl.h" #include "gcpriv.h" @@ -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; } @@ -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. @@ -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(); + // fire_mark_event (ETW::???, current_promoted_bytes, last_promoted_bytes); +#endif // FEATURE_GCBRIDGE + if (!full_p) { #ifdef USE_REGIONS @@ -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. diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl index 898770f717d324..a391dd68a829f3 100644 --- a/src/coreclr/gc/gcenv.ee.standalone.inl +++ b/src/coreclr/gc/gcenv.ee.standalone.inl @@ -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); diff --git a/src/coreclr/gc/gchandletable.cpp b/src/coreclr/gc/gchandletable.cpp index ef0bbf8c93aef7..d503aca269b6cd 100644 --- a/src/coreclr/gc/gchandletable.cpp +++ b/src/coreclr/gc/gchandletable.cpp @@ -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; @@ -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(type); } diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h index afc29e837a9146..0f66dabc66545a 100644 --- a/src/coreclr/gc/gcinterface.ee.h +++ b/src/coreclr/gc/gcinterface.ee.h @@ -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 @@ -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 diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index bb1e5bc173e1b7..c65485ab76a036 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -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__ @@ -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 diff --git a/src/coreclr/gc/gcscan.cpp b/src/coreclr/gc/gcscan.cpp index 95dab9e11c519f..6d4f73a1728e67 100644 --- a/src/coreclr/gc/gcscan.cpp +++ b/src/coreclr/gc/gcscan.cpp @@ -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 */ diff --git a/src/coreclr/gc/gcscan.h b/src/coreclr/gc/gcscan.h index f569d9ff2a8f3b..6367e3c87b8984 100644 --- a/src/coreclr/gc/gcscan.h +++ b/src/coreclr/gc/gcscan.h @@ -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 (); diff --git a/src/coreclr/gc/handletableconstants.h b/src/coreclr/gc/handletableconstants.h index a334cc7d5646c6..a838016f227536 100644 --- a/src/coreclr/gc/handletableconstants.h +++ b/src/coreclr/gc/handletableconstants.h @@ -14,7 +14,7 @@ #endif #define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10 -#define HANDLE_MAX_INTERNAL_TYPES 12 +#define HANDLE_MAX_INTERNAL_TYPES 16 /*--------------------------------------------------------------------------*/ diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp index 6f3bb7a52fa338..d580c9ee9964c3 100644 --- a/src/coreclr/gc/objecthandle.cpp +++ b/src/coreclr/gc/objecthandle.cpp @@ -478,7 +478,10 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt case HNDTYPE_STRONG: #ifdef FEATURE_SIZED_REF_HANDLES case HNDTYPE_SIZEDREF: -#endif +#endif // FEATURE_SIZED_REF_HANDLES +#ifdef FEATURE_GCBRIDGE + case HNDTYPE_CROSSREFERENCE: +#endif // FEATURE_GCBRIDGE break; case HNDTYPE_PINNED: @@ -572,6 +575,7 @@ static const uint32_t s_rgTypeFlags[] = HNDF_EXTRAINFO, // HNDTYPE_SIZEDREF HNDF_EXTRAINFO, // HNDTYPE_WEAK_NATIVE_COM HNDF_EXTRAINFO, // HNDTYPE_WEAK_INTERIOR_POINTER + HNDF_EXTRAINFO, // HNDTYPE_CROSSREFERENCE }; int getNumberOfSlots() @@ -1529,6 +1533,162 @@ void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* s } #endif // FEATURE_SIZED_REF_HANDLES +#ifdef FEATURE_GCBRIDGE + +// [REMOVE] This is for a prototype implementation of the SCC algorithm. +class CrossReferenceList final +{ +private: + struct Node + { + _UNCHECKED_OBJECTREF* pObjRef; + uintptr_t pExtraInfo; + Node* next; + }; + + Node* head; + size_t count; + +public: + CrossReferenceList() : head(nullptr), count(0) {} + + ~CrossReferenceList() + { + Node* current = head; + while (current != nullptr) + { + Node* next = current->next; + delete current; + current = next; + } + } + + void Add(_UNCHECKED_OBJECTREF* pObjRef, uintptr_t pExtraInfo) + { + Node* newNode = new Node{ pObjRef, pExtraInfo, head }; + head = newNode; + ++count; + } + + size_t Count() const + { + return count; + } + + class Iterator + { + private: + Node* current; + + public: + Iterator(Node* start) : current(start) {} + + bool operator!=(const Iterator& other) const + { + return current != other.current; + } + + void operator++() + { + if (current != nullptr) + current = current->next; + } + + std::pair<_UNCHECKED_OBJECTREF*, uintptr_t> operator*() const + { + return { current->pObjRef, current->pExtraInfo }; + } + }; + + Iterator begin() const + { + return Iterator(head); + } + + Iterator end() const + { + return Iterator(nullptr); + } +}; + +void CALLBACK CollectCrossReferenceEntries(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2) +{ + WRAPPER_NO_CONTRACT; + _ASSERTE(pObjRef != NULL); + _ASSERTE(lp1 != NULL); + + Object** pRef = (Object** )pObjRef; + _ASSERTE(*pRef != NULL); + + // The object is already marked, so we don't need to ask the bridge about it. + if (g_theGCHeap->IsPromoted(*pRef)) + return; + + // The object is not marked, so we need to ask the bridge about it. + CrossReferenceList* list = (CrossReferenceList*)lp1; + list->Add(pObjRef, *pExtraInfo); +} + +void Ref_TraceGCBridge(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn) +{ + WRAPPER_NO_CONTRACT; + _ASSERTE(!sc->concurrent); + + uint32_t types[] = + { + HNDTYPE_CROSSREFERENCE + }; + uint32_t flags = HNDGCF_NORMAL | HNDGCF_EXTRAINFO; + + CrossReferenceList list; + HandleTableMap *walk = &g_HandleTableMap; + while (walk) + { + for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++) + { + if (walk->pBuckets[i] != NULL) + { + int uCPUindex = getSlotNumber(sc); + int uCPUlimit = getNumberOfSlots(); + assert(uCPUlimit > 0); + int uCPUstep = getThreadCount(sc); + HHANDLETABLE* pTable = walk->pBuckets[i]->pTable; + for ( ; uCPUindex < uCPUlimit; uCPUindex += uCPUstep) + { + HHANDLETABLE hTable = pTable[uCPUindex]; + if (hTable) + HndScanHandlesForGC(hTable, CollectCrossReferenceEntries, (uintptr_t)&list, 0, types, ARRAY_SIZE(types), condemned, maxgen, flags); + } + } + } + walk = walk->pNext; + } + + if (list.Count() != 0) + { + // [TODO] Implement the SCC algorithm. + size_t sccsCount = 1; + StronglyConnectedComponent* sccs = (StronglyConnectedComponent*)malloc(sizeof(StronglyConnectedComponent) * sccsCount); + sccs->Count = list.Count(); + sccs->Context = (uintptr_t*)malloc(sizeof(uintptr_t) * sccs->Count); + + uintptr_t* curr = sccs->Context; + for (auto& entry : list) + { + // Promote the object and let the bridge indicate when the + // object is actually dead. + fn(entry.first, sc, 0); + + *curr = entry.second; + curr++; + } + + // The callee here will free the allocated memory. + GCToEEInterface::TriggerGCBridge(sccsCount, sccs, 0, nullptr); + } +} +#endif // FEATURE_GCBRIDGE + void Ref_CheckAlive(uint32_t condemned, uint32_t maxgen, ScanContext *sc) { WRAPPER_NO_CONTRACT; @@ -1613,6 +1773,9 @@ void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Re #endif #ifdef FEATURE_SIZED_REF_HANDLES HNDTYPE_SIZEDREF, +#endif +#ifdef FEATURE_GCBRIDGE + HNDTYPE_CROSSREFERENCE, #endif }; @@ -1679,7 +1842,10 @@ void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_sca #ifdef FEATURE_SIZED_REF_HANDLES HNDTYPE_SIZEDREF, #endif - HNDTYPE_WEAK_INTERIOR_POINTER + HNDTYPE_WEAK_INTERIOR_POINTER, +#ifdef FEATURE_GCBRIDGE + HNDTYPE_CROSSREFERENCE, +#endif }; uint32_t flags = HNDGCF_NORMAL; @@ -1804,7 +1970,10 @@ void Ref_AgeHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc) #ifdef FEATURE_SIZED_REF_HANDLES HNDTYPE_SIZEDREF, #endif - HNDTYPE_WEAK_INTERIOR_POINTER + HNDTYPE_WEAK_INTERIOR_POINTER, +#ifdef FEATURE_GCBRIDGE + HNDTYPE_CROSSREFERENCE, +#endif }; // perform a multi-type scan that ages the handles @@ -1861,7 +2030,10 @@ void Ref_RejuvenateHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc) #ifdef FEATURE_SIZED_REF_HANDLES HNDTYPE_SIZEDREF, #endif - HNDTYPE_WEAK_INTERIOR_POINTER + HNDTYPE_WEAK_INTERIOR_POINTER, +#ifdef FEATURE_GCBRIDGE + HNDTYPE_CROSSREFERENCE, +#endif }; // reset the ages of these handles @@ -1917,7 +2089,10 @@ void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc) HNDTYPE_SIZEDREF, #endif HNDTYPE_DEPENDENT, - HNDTYPE_WEAK_INTERIOR_POINTER + HNDTYPE_WEAK_INTERIOR_POINTER, +#ifdef FEATURE_GCBRIDGE + HNDTYPE_CROSSREFERENCE, +#endif }; // verify these handles diff --git a/src/coreclr/gc/objecthandle.h b/src/coreclr/gc/objecthandle.h index 634510758c322b..2f2f7a023bee50 100644 --- a/src/coreclr/gc/objecthandle.h +++ b/src/coreclr/gc/objecthandle.h @@ -104,6 +104,9 @@ void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, Sc void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); void Ref_ScanWeakInteriorPointersForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); +#ifdef FEATURE_GCBRIDGE +void Ref_TraceGCBridge(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); +#endif // FEATURE_GCBRIDGE void Ref_CheckReachable (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, ScanContext* sc); void Ref_CheckAlive (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, ScanContext *sc); diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp index 4aba23c9a077dc..8d0d4805d01506 100644 --- a/src/coreclr/gc/sample/gcenv.ee.cpp +++ b/src/coreclr/gc/sample/gcenv.ee.cpp @@ -164,6 +164,10 @@ bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject) return false; } +void GCToEEInterface::TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs) +{ +} + bool GCToEEInterface::IsPreemptiveGCDisabled() { Thread* pThread = ::GetThread(); diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props index 52f437404776f3..d761ff4eb07301 100644 --- a/src/coreclr/nativeaot/Directory.Build.props +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -71,7 +71,13 @@ FEATURE_PERFTRACING;$(DefineConstants) - + + + true + + + FEATURE_JAVAMARSHAL;FEATURE_GCBRIDGE;$(DefineConstants) + diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index ed401ea0ab99aa..61b1cf56f092e7 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -541,6 +541,12 @@ if(FEATURE_OBJCMARSHAL) ) endif(FEATURE_OBJCMARSHAL) +if(FEATURE_JAVAMARSHAL) + list(APPEND VM_SOURCES_WKS + interoplibinterface_java.cpp + ) +endif(FEATURE_JAVAMARSHAL) + if(CLR_CMAKE_TARGET_WIN32) set(VM_SOURCES_DAC_AND_WKS_WIN32 diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 118daed24af7ae..fc886d877affd9 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -784,6 +784,12 @@ class AppDomain final return ::CreateWeakInteriorHandle(m_handleStore, object, pInteriorPointerLocation); } + OBJECTHANDLE CreateCrossReferenceHandle(OBJECTREF object, void* userContext) + { + WRAPPER_NO_CONTRACT; + return ::CreateCrossReferenceHandle(m_handleStore, object, userContext); + } + #if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) OBJECTHANDLE CreateRefcountedHandle(OBJECTREF object) { diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 4738b0d7aeb8fa..fd0ad1d276e2bd 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -400,6 +400,20 @@ bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject) return false; } +void GCToEEInterface::TriggerGCBridge(size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifdef FEATURE_GCBRIDGE + Interop::TriggerGCBridge(sccsLen, sccs, ccrsLen, ccrs); +#endif // FEATURE_GCBRIDGE +} + void GCToEEInterface::SyncBlockCacheDemote(int max_gen) { CONTRACTL diff --git a/src/coreclr/vm/gchandleutilities.h b/src/coreclr/vm/gchandleutilities.h index ea1a826c9ddd82..f6f08499222206 100644 --- a/src/coreclr/vm/gchandleutilities.h +++ b/src/coreclr/vm/gchandleutilities.h @@ -150,6 +150,18 @@ inline OBJECTHANDLE CreateWeakInteriorHandle(IGCHandleStore* store, OBJECTREF pr return hnd; } +inline OBJECTHANDLE CreateCrossReferenceHandle(IGCHandleStore* store, OBJECTREF primary, void* userContext) +{ + OBJECTHANDLE hnd = store->CreateHandleWithExtraInfo(OBJECTREFToObject(primary), HNDTYPE_CROSSREFERENCE, userContext); + if (!hnd) + { + COMPlusThrowOM(); + } + + DiagHandleCreated(hnd, primary); + return hnd; +} + // Global handle creation convenience functions inline OBJECTHANDLE CreateGlobalHandleCommon(OBJECTREF object, HandleType type) { diff --git a/src/coreclr/vm/interoplibinterface.h b/src/coreclr/vm/interoplibinterface.h index c4f23ce654016c..7a1273bb3a06d4 100644 --- a/src/coreclr/vm/interoplibinterface.h +++ b/src/coreclr/vm/interoplibinterface.h @@ -153,7 +153,6 @@ class ObjCMarshalNative static void OnEnteredFinalizerQueue(_In_ OBJECTREF object); }; - extern "C" BOOL QCALLTYPE ObjCMarshal_TryInitializeReferenceTracker( _In_ ObjCMarshalNative::BeginEndCallback beginEndCallback, _In_ ObjCMarshalNative::IsReferencedCallback isReferencedCallback, @@ -169,6 +168,34 @@ extern "C" BOOL QCALLTYPE ObjCMarshal_TrySetGlobalMessageSendCallback( _In_ void* fptr); #endif // FEATURE_OBJCMARSHAL +#ifdef FEATURE_JAVAMARSHAL +class JavaNative +{ +public: // GC interaction + static bool TriggerGCBridge( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ size_t ccrsLen, + _In_ ComponentCrossReference* ccrs); +}; + +extern "C" BOOL QCALLTYPE JavaMarshal_Initialize( + _In_ void* markCrossReferences); + +extern "C" void* QCALLTYPE JavaMarshal_CreateReferenceTrackingHandle( + _In_ QCall::ObjectHandleOnStack obj, + _In_ void* context); + +extern "C" void QCALLTYPE JavaMarshal_ReleaseMarkCrossReferenceResources( + _In_ int32_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ ComponentCrossReference* ccrs); + +extern "C" BOOL QCALLTYPE JavaMarshal_GetContext( + _In_ OBJECTHANDLE handle, + _Out_ void** context); +#endif // FEATURE_JAVAMARSHAL + class Interop { public: @@ -198,6 +225,19 @@ class Interop // and OnGCFinished. static void OnBeforeGCScanRoots(_In_ bool isConcurrent); static void OnAfterGCScanRoots(_In_ bool isConcurrent); + +#ifdef FEATURE_GCBRIDGE + static void TriggerGCBridge( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ size_t ccrsLen, + _In_ ComponentCrossReference* ccrs); + + static void ReleaseGCBridgeArguments( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ ComponentCrossReference* ccrs); +#endif // FEATURE_GCBRIDGE }; #endif // _INTEROPLIBINTERFACE_H_ diff --git a/src/coreclr/vm/interoplibinterface_java.cpp b/src/coreclr/vm/interoplibinterface_java.cpp new file mode 100644 index 00000000000000..641819541782c4 --- /dev/null +++ b/src/coreclr/vm/interoplibinterface_java.cpp @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef FEATURE_JAVAMARSHAL + +// Runtime headers +#include "common.h" + +// Interop library header +#include + +#include "interoplibinterface.h" + +using CrossreferenceHandleCallback = void(STDMETHODCALLTYPE *)(size_t, StronglyConnectedComponent*, size_t, ComponentCrossReference*); + +namespace +{ + BOOL g_Initialized; + CrossreferenceHandleCallback g_MarkCrossReferences; +} + +extern "C" BOOL QCALLTYPE JavaMarshal_Initialize( + _In_ void* markCrossReferences) +{ + QCALL_CONTRACT; + _ASSERTE(markCrossReferences != NULL); + + BOOL success = FALSE; + + BEGIN_QCALL; + + // Switch to Cooperative mode since we are setting callbacks that + // will be used during a GC and we want to ensure a GC isn't occurring + // while they are being set. + { + GCX_COOP(); + if (InterlockedCompareExchange((LONG*)&g_Initialized, TRUE, FALSE) == FALSE) + { + g_MarkCrossReferences = (CrossreferenceHandleCallback)markCrossReferences; + success = TRUE; + } + } + + END_QCALL; + + return success; +} + +extern "C" void* QCALLTYPE JavaMarshal_CreateReferenceTrackingHandle( + _In_ QCall::ObjectHandleOnStack obj, + _In_ void* context) +{ + QCALL_CONTRACT; + + OBJECTHANDLE instHandle = NULL; + + BEGIN_QCALL; + + GCX_COOP(); + instHandle = GetAppDomain()->CreateCrossReferenceHandle(obj.Get(), context); + + END_QCALL; + + return (void*)instHandle; +} + +extern "C" void QCALLTYPE JavaMarshal_ReleaseMarkCrossReferenceResources( + _In_ int32_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ ComponentCrossReference* ccrs) +{ + QCALL_CONTRACT; + _ASSERTE(sccsLen >= 0); + + BEGIN_QCALL; + + Interop::ReleaseGCBridgeArguments(sccsLen, sccs, ccrs); + + END_QCALL; +} + +extern "C" BOOL QCALLTYPE JavaMarshal_GetContext( + _In_ OBJECTHANDLE handle, + _Out_ void** context) +{ + QCALL_CONTRACT_NO_GC_TRANSITION; + _ASSERTE(handle != NULL); + _ASSERTE(context != NULL); + + IGCHandleManager* mgr = GCHandleUtilities::GetGCHandleManager(); + HandleType type = mgr->HandleFetchType(handle); + if (type != HNDTYPE_CROSSREFERENCE) + return FALSE; + + *context = mgr->GetExtraInfoFromHandle(handle); + return TRUE; +} + +bool JavaNative::TriggerGCBridge( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ size_t ccrsLen, + _In_ ComponentCrossReference* ccrs) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Not initialized + if (g_MarkCrossReferences == NULL) + return false; + + g_MarkCrossReferences(sccsLen, sccs, ccrsLen, ccrs); + return true; +} + +#endif // FEATURE_JAVAMARSHAL diff --git a/src/coreclr/vm/interoplibinterface_shared.cpp b/src/coreclr/vm/interoplibinterface_shared.cpp index f31b128e47cfb1..5f11879fb05dc3 100644 --- a/src/coreclr/vm/interoplibinterface_shared.cpp +++ b/src/coreclr/vm/interoplibinterface_shared.cpp @@ -154,3 +154,93 @@ void Interop::OnAfterGCScanRoots(_In_ bool isConcurrent) ObjCMarshalNative::AfterRefCountedHandleCallbacks(); #endif // FEATURE_OBJCMARSHAL } + +#ifdef FEATURE_GCBRIDGE + +namespace +{ + Volatile g_GCBridgeActive = FALSE; + + void ReleaseGCBridgeArgumentsWorker( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ ComponentCrossReference* ccrs) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Memory was allocated for the collections by the GC. + // See callers of GCToEEInterface::TriggerGCBridge(). + + // Free memory in each of the SCCs + for (size_t i = 0; i < sccsLen; i++) + { + free(sccs[i].Context); + } + free(sccs); + free(ccrs); + } +} + +void Interop::TriggerGCBridge( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ size_t ccrsLen, + _In_ ComponentCrossReference* ccrs) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if (g_GCBridgeActive) + { + // Release the memory allocated since the GCBridge + // is already running and we're not passing them to it. + ReleaseGCBridgeArgumentsWorker(sccsLen, sccs, ccrs); + return; + } + + bool gcBridgeTriggered; + +#ifdef FEATURE_JAVAMARSHAL + gcBridgeTriggered = JavaNative::TriggerGCBridge(sccsLen, sccs, ccrsLen, ccrs); +#endif // FEATURE_JAVAMARSHAL + + if (!gcBridgeTriggered) + { + // Release the memory allocated since the GCBridge + // wasn't trigger for some reason. + ReleaseGCBridgeArgumentsWorker(sccsLen, sccs, ccrs); + return; + } + + // Mark the GCBridge as active. + g_GCBridgeActive = TRUE; +} + +void Interop::ReleaseGCBridgeArguments( + _In_ size_t sccsLen, + _In_ StronglyConnectedComponent* sccs, + _In_ ComponentCrossReference* ccrs) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(g_GCBridgeActive); + + // Mark the GCBridge as inactive. + // This much be synchronized with the GC so switch to cooperative mode. + { + GCX_COOP(); + g_GCBridgeActive = FALSE; + } + + ReleaseGCBridgeArgumentsWorker(sccsLen, sccs, ccrs); +} + +#endif // FEATURE_GCBRIDGE diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 19ce7a5ffa05ab..016df694826043 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -436,6 +436,12 @@ static const Entry s_QCall[] = DllImportEntry(ObjCMarshal_TryInitializeReferenceTracker) DllImportEntry(ObjCMarshal_CreateReferenceTrackingHandle) #endif +#if defined(FEATURE_JAVAMARSHAL) + DllImportEntry(JavaMarshal_Initialize) + DllImportEntry(JavaMarshal_CreateReferenceTrackingHandle) + DllImportEntry(JavaMarshal_ReleaseMarkCrossReferenceResources) + DllImportEntry(JavaMarshal_GetContext) +#endif #if defined(FEATURE_EVENTSOURCE_XPLAT) DllImportEntry(IsEventSourceLoggingEnabled) DllImportEntry(LogEventSource) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 487028d306a3fa..8f4a539e03fb73 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3908,6 +3908,12 @@ Attempt to track an Objective-C Type without a finalizer. + + Attempt to reinitialize JavaMarshal. + + + Attempt to get context from an unsupport GCHandle type. + Supplying a non-null inner should also be marked as Aggregated. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9de275bad2cbc8..11c6c6d898f067 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2772,6 +2772,11 @@ + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/ComponentCrossReference.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/ComponentCrossReference.cs new file mode 100644 index 00000000000000..b3465d9998215d --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/ComponentCrossReference.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices.Java +{ + [SupportedOSPlatform("android")] + public struct ComponentCrossReference + { + /// + /// Index of the source group. + /// + public nint SourceGroupIndex; + + /// + /// Index of the destination group. + /// + public nint DestinationGroupIndex; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.cs new file mode 100644 index 00000000000000..3e4193fd826e9e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices.Java +{ + [CLSCompliant(false)] + [SupportedOSPlatform("android")] + public static partial class JavaMarshal + { + public static unsafe void Initialize( + // Callback used to perform the marking of SCCs. + delegate* unmanaged< + nint, // Length of SCC collection + StronglyConnectedComponent*, // SCC collection + nint, // Length of CCR collection + ComponentCrossReference*, // CCR collection + void> markCrossReferences) + { +#if NATIVEAOT + throw new NotImplementedException(); +#elif MONO + throw new NotSupportedException(); +#else + ArgumentNullException.ThrowIfNull(markCrossReferences); + + if (!InitializeInternal((IntPtr)markCrossReferences)) + { + throw new InvalidOperationException(SR.InvalidOperation_ReinitializeJavaMarshal); + } +#endif + } + + public static GCHandle CreateReferenceTrackingHandle(object obj, IntPtr context) + { +#if NATIVEAOT + throw new NotImplementedException(); +#elif MONO + throw new NotSupportedException(); +#else + ArgumentNullException.ThrowIfNull(obj); + + IntPtr handle = CreateReferenceTrackingHandleInternal(ObjectHandleOnStack.Create(ref obj), context); + return GCHandle.FromIntPtr(handle); +#endif + } + + public static IntPtr GetContext(GCHandle obj) + { +#if NATIVEAOT + throw new NotImplementedException(); +#elif MONO + throw new NotSupportedException(); +#else + IntPtr handle = GCHandle.ToIntPtr(obj); + if (handle == IntPtr.Zero + || !GetContextInternal(handle, out IntPtr context)) + { + throw new InvalidOperationException(SR.InvalidOperation_IncorrectGCHandleType); + } + + return context; +#endif + } + + public static unsafe void ReleaseMarkCrossReferenceResources( + Span sccs, + Span ccrs) + { +#if NATIVEAOT + throw new NotImplementedException(); +#elif MONO + throw new NotSupportedException(); +#else + ReleaseMarkCrossReferenceResources( + sccs.Length, + Unsafe.AsPointer(ref MemoryMarshal.GetReference(sccs)), + Unsafe.AsPointer(ref MemoryMarshal.GetReference(ccrs))); +#endif + } + +#if CORECLR + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "JavaMarshal_Initialize")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool InitializeInternal(IntPtr callback); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "JavaMarshal_CreateReferenceTrackingHandle")] + private static partial IntPtr CreateReferenceTrackingHandleInternal(ObjectHandleOnStack obj, IntPtr context); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "JavaMarshal_ReleaseMarkCrossReferenceResources")] + private static unsafe partial void ReleaseMarkCrossReferenceResources(int length, void* sccs, void* ccrs); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "JavaMarshal_GetContext")] + [SuppressGCTransition] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool GetContextInternal(IntPtr handle, out IntPtr context); +#endif + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/StronglyConnectedComponent.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/StronglyConnectedComponent.cs new file mode 100644 index 00000000000000..f9cc049962e804 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/StronglyConnectedComponent.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices.Java +{ + [CLSCompliant(false)] + [SupportedOSPlatform("android")] + public unsafe struct StronglyConnectedComponent + { + /// + /// Number of objects in each collection. + /// + public nint Count; + + /// + /// Contains pointers to context passed during + /// creation of each GCHandle. + /// + public IntPtr* Context; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index eb2130e4f80bc8..133fcf75a1c5ec 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -2366,6 +2366,38 @@ public enum VARKIND VAR_DISPATCH = 3, } } +namespace System.Runtime.InteropServices.Java +{ + [System.Runtime.Versioning.SupportedOSPlatform("android")] + public struct ComponentCrossReference + { + public System.IntPtr SourceGroupIndex; + public System.IntPtr DestinationGroupIndex; + } + [System.Runtime.Versioning.SupportedOSPlatform("android")] + [System.CLSCompliantAttribute(false)] + public static class JavaMarshal + { + public static unsafe void Initialize( + delegate* unmanaged< + System.IntPtr, + StronglyConnectedComponent*, + System.IntPtr, + ComponentCrossReference*, + void> markCrossReferences) => throw null; + + public static GCHandle CreateReferenceTrackingHandle(object obj, System.IntPtr context) => throw null; + public static System.IntPtr GetContext(GCHandle obj) => throw null; + public static unsafe void ReleaseMarkCrossReferenceResources(System.Span sccs, System.Span ccrs) => throw null; + } + [System.Runtime.Versioning.SupportedOSPlatform("android")] + [System.CLSCompliantAttribute(false)] + public unsafe struct StronglyConnectedComponent + { + public System.IntPtr Count; + public System.IntPtr* Context; + } +} namespace System.Runtime.InteropServices.ObjectiveC { [System.Runtime.Versioning.SupportedOSPlatformAttribute("macos")]