diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs index 53819b2cc59cec..5e4ff43a3625c9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs @@ -52,9 +52,16 @@ public partial struct DependentHandle : IDisposable // // This struct intentionally does no self-synchronization. It's up to the caller to // to use DependentHandles in a thread-safe way. + // + // There are two types of dependant handles: "normal" and "defer finalize". The "defer + // finalize" version promotes the secondary until the primary has been completely collected. + // The low order bit of the handle is stolen to indicate that the handle is the "defer finalize" + // flavor. It should be masked off before using. // ========================================================================================= - private IntPtr _handle; + private const nint IsDeferFinalizeBit = 1; + + private nint _taggedHandle; /// /// Initializes a new instance of the struct with the specified arguments. @@ -63,10 +70,25 @@ public partial struct DependentHandle : IDisposable /// The dependent object instance to associate with . public DependentHandle(object? target, object? dependent) { - IntPtr handle = InternalAlloc(target, dependent); + IntPtr handle = InternalAlloc(false, target, dependent); + if (handle == 0) + handle = InternalAllocWithGCTransition(false, target, dependent); + _taggedHandle = handle; + } + + /// + /// Initializes a new instance of the struct with the specified arguments. + /// + /// If true, the dependent is not finalized until the target is complexly collected. + /// The target object instance to track. + /// The dependent object instance to associate with . + internal DependentHandle(bool isDeferFinalize, object? target, object? dependent) + { + // no need to check for null result: InternalInitialize expected to throw OOM. + IntPtr handle = InternalAlloc(isDeferFinalize, target, dependent); if (handle == 0) - handle = InternalAllocWithGCTransition(target, dependent); - _handle = handle; + handle = InternalAllocWithGCTransition(isDeferFinalize, target, dependent); + _taggedHandle = handle | (isDeferFinalize ? IsDeferFinalizeBit : 0); } /// @@ -74,7 +96,7 @@ public DependentHandle(object? target, object? dependent) /// and has not yet been disposed. /// /// This property is thread-safe. - public readonly bool IsAllocated => _handle != 0; + public readonly bool IsAllocated => _taggedHandle != 0; /// /// Gets or sets the target object instance for the current handle. The target can only be set to a value @@ -88,7 +110,7 @@ public object? Target { readonly get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; if (handle == 0) { @@ -99,7 +121,7 @@ readonly get } set { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; if (handle == 0 || value is not null) { @@ -125,7 +147,7 @@ public object? Dependent { readonly get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; if (handle == 0) { @@ -136,14 +158,16 @@ readonly get } set { - IntPtr handle = _handle; + nint taggedHandle = _taggedHandle; - if (handle == 0) + if (taggedHandle == 0) { ThrowHelper.ThrowInvalidOperationException(); } - InternalSetDependent(handle, value); + nint handle = taggedHandle & ~IsDeferFinalizeBit; + bool isDeferFinalize = (taggedHandle & IsDeferFinalizeBit) != 0; + InternalSetDependent(isDeferFinalize, handle, value); } } @@ -161,7 +185,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent { get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; if (handle == 0) { @@ -181,7 +205,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent /// This method mirrors , but without the allocation check. internal readonly object? UnsafeGetTarget() { - return InternalGetTarget(_handle); + return InternalGetTarget(_taggedHandle & ~IsDeferFinalizeBit); } /// @@ -196,7 +220,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent /// internal readonly object? UnsafeGetTargetAndDependent(out object? dependent) { - return InternalGetTargetAndDependent(_handle, out dependent); + return InternalGetTargetAndDependent(_taggedHandle & ~IsDeferFinalizeBit, out dependent); } /// @@ -205,7 +229,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent /// This method mirrors the setter, but without allocation and input checks. internal readonly void UnsafeSetTargetToNull() { - InternalSetTargetToNull(_handle); + InternalSetTargetToNull(_taggedHandle & ~IsDeferFinalizeBit); } /// @@ -214,7 +238,9 @@ internal readonly void UnsafeSetTargetToNull() /// This method mirrors , but without the allocation check. internal readonly void UnsafeSetDependent(object? dependent) { - InternalSetDependent(_handle, dependent); + nint handle = _taggedHandle & ~IsDeferFinalizeBit; + bool isDeferFinalize = (_taggedHandle & IsDeferFinalizeBit) != 0; + InternalSetDependent(isDeferFinalize, handle, dependent); } /// @@ -223,61 +249,63 @@ public void Dispose() { // Forces the DependentHandle back to non-allocated state // (if not already there) and frees the handle if needed. - IntPtr handle = _handle; + nint taggedHandle = _taggedHandle; - if (handle != 0) + if (taggedHandle != 0) { - _handle = 0; + _taggedHandle = 0; - if (!InternalFree(handle)) + nint handle = taggedHandle & ~IsDeferFinalizeBit; + bool isDeferFinalize = (taggedHandle & IsDeferFinalizeBit) != 0; + if (!InternalFree(isDeferFinalize, handle)) { - InternalFreeWithGCTransition(handle); + InternalFreeWithGCTransition(isDeferFinalize, handle); } } } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr InternalAlloc(object? target, object? dependent); + private static extern IntPtr InternalAlloc(bool isDeferFinalize, object? target, object? dependent); [MethodImpl(MethodImplOptions.NoInlining)] - private static IntPtr InternalAllocWithGCTransition(object? target, object? dependent) - => _InternalAllocWithGCTransition(ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref dependent)); + private static IntPtr InternalAllocWithGCTransition(bool isDeferFinalize, object? target, object? dependent) + => _InternalAllocWithGCTransition(isDeferFinalize, ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref dependent)); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalAllocWithGCTransition")] - private static partial IntPtr _InternalAllocWithGCTransition(ObjectHandleOnStack target, ObjectHandleOnStack dependent); + private static partial IntPtr _InternalAllocWithGCTransition([MarshalAs(UnmanagedType.Bool)] bool isDeferFinalize, ObjectHandleOnStack target, ObjectHandleOnStack dependent); #if DEBUG [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object? InternalGetTarget(IntPtr dependentHandle); + private static extern object? InternalGetTarget(nint dependentHandle); #else // This optimization is the same that is used in GCHandle in RELEASE mode. // This is not used in DEBUG builds as the runtime performs additional checks. // The logic below is the inlined copy of ObjectFromHandle in the unmanaged runtime. #pragma warning disable 8500 // address of managed types - private static unsafe object? InternalGetTarget(IntPtr dependentHandle) => *(object*)dependentHandle; + private static unsafe object? InternalGetTarget(nint dependentHandle) => *(object*)dependentHandle; #pragma warning restore 8500 #endif [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object? InternalGetDependent(IntPtr dependentHandle); + private static extern object? InternalGetDependent(nint dependentHandle); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object? InternalGetTargetAndDependent(IntPtr dependentHandle, out object? dependent); + private static extern object? InternalGetTargetAndDependent(nint dependentHandle, out object? dependent); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void InternalSetDependent(IntPtr dependentHandle, object? dependent); + private static extern void InternalSetDependent(bool isDeferFinalize, nint dependentHandle, object? dependent); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void InternalSetTargetToNull(IntPtr dependentHandle); + private static extern void InternalSetTargetToNull(nint dependentHandle); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool InternalFree(IntPtr dependentHandle); + private static extern bool InternalFree(bool isDeferFinalize, IntPtr dependentHandle); [MethodImpl(MethodImplOptions.NoInlining)] - private static void InternalFreeWithGCTransition(IntPtr dependentHandle) - => _InternalFreeWithGCTransition(dependentHandle); + private static void InternalFreeWithGCTransition(bool isDeferFinalize, IntPtr dependentHandle) + => _InternalFreeWithGCTransition(isDeferFinalize, dependentHandle); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalFreeWithGCTransition")] - private static partial void _InternalFreeWithGCTransition(IntPtr dependentHandle); + private static partial void _InternalFreeWithGCTransition([MarshalAs(UnmanagedType.Bool)] bool isDeferFinalize, IntPtr dependentHandle); } } diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index e79dab808def3f..5bbaf76ee73c3a 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7740,7 +7740,7 @@ void CALLBACK DacHandleWalker::EnumCallback(PTR_UNCHECKED_OBJECTREF handle, uint data.Handle = TO_CDADDR(handle.GetAddr()); data.Type = param->Type; - if (param->Type == HNDTYPE_DEPENDENT) + if (param->Type == HNDTYPE_DEPENDENT || param->Type == HNDTYPE_DEPENDENT_DEFER_FINALIZE) data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); else data.Secondary = 0; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index b316a0769d3f67..d1aa37cb603205 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -7582,6 +7582,9 @@ UINT32 DacRefWalker::GetHandleWalkerMask() if (mHandleMask & CorHandleStrongSizedByref) result |= (1 << HNDTYPE_SIZEDREF); + if (mHandleMask & CorHandleStrongDependentDeferFinalize) + result |= (1 << HNDTYPE_DEPENDENT_DEFER_FINALIZE); + return result; } @@ -7721,6 +7724,11 @@ HRESULT DacHandleWalker::Next(ULONG count, DacGcReference roots[], ULONG *pFetch case HNDTYPE_SIZEDREF: roots[i].dwType = (DWORD)CorHandleStrongSizedByref; break; + + case HNDTYPE_DEPENDENT_DEFER_FINALIZE: + roots[i].dwType = (DWORD)CorHandleStrongDependentDeferFinalize; + roots[i].i64ExtraData = GetDependentHandleSecondary(CLRDATA_ADDRESS_TO_TADDR(handle.Handle)).GetAddr(); + break; } } diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 3993d8a6f52ee4..2621d69e7ba63b 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3255,7 +3255,7 @@ ClrDataAccess::GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int ind HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum) { unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_DEPENDENT, - HNDTYPE_SIZEDREF, + HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT_DEFER_FINALIZE, #if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL @@ -3293,7 +3293,7 @@ HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppH SOSDacEnter(); unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_DEPENDENT, - HNDTYPE_SIZEDREF, + HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT_DEFER_FINALIZE, #if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL diff --git a/src/coreclr/gc/gchandletable.cpp b/src/coreclr/gc/gchandletable.cpp index a4b3ab8629a0da..fbfc4b4d1e5400 100644 --- a/src/coreclr/gc/gchandletable.cpp +++ b/src/coreclr/gc/gchandletable.cpp @@ -89,16 +89,18 @@ OBJECTHANDLE GCHandleStore::CreateHandleWithExtraInfo(Object* object, HandleType return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object), reinterpret_cast(pExtraInfo)); } -OBJECTHANDLE GCHandleStore::CreateDependentHandle(Object* primary, Object* secondary) +OBJECTHANDLE GCHandleStore::CreateDependentHandle(HandleType type, Object* primary, Object* secondary) { + _ASSERTE(type == HNDTYPE_DEPENDENT || type == HNDTYPE_DEPENDENT_DEFER_FINALIZE); + HHANDLETABLE handletable = _underlyingBucket.pTable[GetCurrentThreadHomeHeapNumber()]; - OBJECTHANDLE handle = ::HndCreateHandle(handletable, HNDTYPE_DEPENDENT, ObjectToOBJECTREF(primary)); + OBJECTHANDLE handle = ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(primary)); if (!handle) { return nullptr; } - ::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(secondary)); + ::SetDependentHandleSecondary(type, handle, ObjectToOBJECTREF(secondary)); return handle; } @@ -195,9 +197,9 @@ bool GCHandleManager::StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* obj return !!::HndFirstAssignHandle(handle, ObjectToOBJECTREF(object)); } -void GCHandleManager::SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object) +void GCHandleManager::SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object) { - ::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(object)); + ::SetDependentHandleSecondary(type, handle, ObjectToOBJECTREF(object)); } Object* GCHandleManager::GetDependentHandleSecondary(OBJECTHANDLE handle) @@ -213,7 +215,7 @@ Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE h HandleType GCHandleManager::HandleFetchType(OBJECTHANDLE handle) { uint32_t type = ::HandleFetchType(handle); - assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM); + assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_DEPENDENT_DEFER_FINALIZE); return static_cast(type); } diff --git a/src/coreclr/gc/gchandletableimpl.h b/src/coreclr/gc/gchandletableimpl.h index 83bcab2b8fd8dd..c2116b0a3f1f04 100644 --- a/src/coreclr/gc/gchandletableimpl.h +++ b/src/coreclr/gc/gchandletableimpl.h @@ -20,7 +20,7 @@ class GCHandleStore : public IGCHandleStore virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo); - virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary); + virtual OBJECTHANDLE CreateDependentHandle(HandleType type, Object* primary, Object* secondary); virtual ~GCHandleStore(); @@ -61,7 +61,7 @@ class GCHandleManager : public IGCHandleManager virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object); - virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object); + virtual void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object); virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 75b84684bb9ed0..802a25a8d2a2f6 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -6,7 +6,7 @@ // The major version of the IGCHeap interface. Breaking changes to this interface // require bumps in the major version number. -#define GC_INTERFACE_MAJOR_VERSION 5 +#define GC_INTERFACE_MAJOR_VERSION 6 // The minor version of the IGCHeap interface. Non-breaking changes are required // to bump the minor version number. GCs and EEs with minor version number @@ -502,7 +502,17 @@ typedef enum * but we are keeping it here for backward compatibility purposes" * */ - HNDTYPE_WEAK_NATIVE_COM = 9 + HNDTYPE_WEAK_NATIVE_COM = 9, + + /* + * DEPENDENT DEFER FINIALIZE HANDLES + * + * Like HNDTYPE_DEPENDENT, but the secondary is always promoted until + * the primary is collected. + * + * + */ + HNDTYPE_DEPENDENT_DEFER_FINALIZE = 10 } HandleType; typedef enum @@ -543,7 +553,7 @@ class IGCHandleStore { virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo) PURE_VIRTUAL - virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) PURE_VIRTUAL + virtual OBJECTHANDLE CreateDependentHandle(HandleType type, Object* primary, Object* secondary) PURE_VIRTUAL virtual ~IGCHandleStore() {}; }; @@ -577,7 +587,7 @@ class IGCHandleManager { virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL - virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL + virtual void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, Object* object) PURE_VIRTUAL virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle) PURE_VIRTUAL diff --git a/src/coreclr/gc/handletable.cpp b/src/coreclr/gc/handletable.cpp index 356b0e0bdc314b..78a87caa25817d 100644 --- a/src/coreclr/gc/handletable.cpp +++ b/src/coreclr/gc/handletable.cpp @@ -597,7 +597,7 @@ void HndWriteBarrierWorker(OBJECTHANDLE handle, _UNCHECKED_OBJECTREF value) } #endif - if (uType == HNDTYPE_DEPENDENT) + if (uType == HNDTYPE_DEPENDENT || uType == HNDTYPE_DEPENDENT_DEFER_FINALIZE) { generation = 0; } diff --git a/src/coreclr/gc/handletablescan.cpp b/src/coreclr/gc/handletablescan.cpp index 7c0692ad5b8ff4..b96893f07d7e8d 100644 --- a/src/coreclr/gc/handletablescan.cpp +++ b/src/coreclr/gc/handletablescan.cpp @@ -978,7 +978,7 @@ void BlockVerifyAgeMapForBlocksWorker(uint32_t *pdwGen, uint32_t dwClumpMask, Sc }); #endif - if (uType == HNDTYPE_DEPENDENT) + if (uType == HNDTYPE_DEPENDENT || uType == HNDTYPE_DEPENDENT_DEFER_FINALIZE) { PTR_uintptr_t pUserData = HandleQuickFetchUserDataPointer((OBJECTHANDLE)pValue); diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp index 1c404c6c23b150..ef42199f1c4322 100644 --- a/src/coreclr/gc/objecthandle.cpp +++ b/src/coreclr/gc/objecthandle.cpp @@ -222,6 +222,45 @@ void CALLBACK PromoteDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *p } } +void CALLBACK PromoteDependentHandleDeferFinalize(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(pExtraInfo); + + Object **pPrimaryRef = (Object **)pObjRef; + Object **pSecondaryRef = (Object **)pExtraInfo; + LOG((LF_GC, LL_INFO1000, "Checking promotion of DependentHandleDeferFinalize\n")); + LOG((LF_GC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tPrimary:\t", pObjRef, "to ", *pObjRef))); + LOG((LF_GC, LL_INFO1000, LOG_HANDLE_OBJECT_CLASS("\tSecondary\t", pSecondaryRef, "to ", *pSecondaryRef))); + + ScanContext *sc = (ScanContext*)lp1; + DhContext *pDhContext = Ref_GetDependentHandleContext(sc); + + if (*pObjRef) + { + if (!g_theGCHeap->IsPromoted(*pPrimaryRef)) + { + // If we see a non-cleared primary which hasn't been promoted, record the fact. We will only require a + // rescan if this flag has been set (if it's clear then the previous scan found only clear and + // promoted handles, so there's no chance of finding an additional handle being promoted on a + // subsequent scan). + pDhContext->m_fUnpromotedPrimaries = true; + } + + if (!g_theGCHeap->IsPromoted(*pSecondaryRef)) + { + LOG((LF_GC, LL_INFO10000, "\tPromoting secondary " LOG_OBJECT_CLASS(*pSecondaryRef))); + _ASSERTE(lp2); + promote_func* callback = (promote_func*) lp2; + callback(pSecondaryRef, (ScanContext *)lp1, 0); + // need to rescan because we might have promoted an object that itself has added fields and this + // promotion might be all that is pinning that object. If we've already scanned that dependent + // handle relationship, we could lose it secondary object. + pDhContext->m_fPromoted = true; + } + } +} + void CALLBACK ClearDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t /*lp1*/, uintptr_t /*lp2*/) { LIMITED_METHOD_CONTRACT; @@ -423,6 +462,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt switch (HandleFetchType(handle)) { case HNDTYPE_DEPENDENT: + case HNDTYPE_DEPENDENT_DEFER_FINALIZE: isDependent = true; break; case HNDTYPE_WEAK_SHORT: @@ -527,6 +567,7 @@ static const uint32_t s_rgTypeFlags[] = HNDF_NORMAL, // HNDTYPE_ASYNCPINNED HNDF_EXTRAINFO, // HNDTYPE_SIZEDREF HNDF_EXTRAINFO, // HNDTYPE_WEAK_NATIVE_COM + HNDF_EXTRAINFO, // HNDTYPE_DEPENDENT_DEFER_FINALIZE }; int getNumberOfSlots() @@ -839,7 +880,7 @@ int getThreadCount(ScanContext* sc) return sc->thread_count; } -void SetDependentHandleSecondary(OBJECTHANDLE handle, OBJECTREF objref) +void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, OBJECTREF objref) { CONTRACTL { @@ -850,6 +891,7 @@ void SetDependentHandleSecondary(OBJECTHANDLE handle, OBJECTREF objref) CONTRACTL_END; // sanity + _ASSERTE(type == HNDTYPE_DEPENDENT || type == HNDTYPE_DEPENDENT_DEFER_FINALIZE); _ASSERTE(handle); #ifdef _DEBUG @@ -864,7 +906,7 @@ void SetDependentHandleSecondary(OBJECTHANDLE handle, OBJECTREF objref) HndWriteBarrier(handle, objref); // store the pointer - HndSetHandleExtraInfo(handle, HNDTYPE_DEPENDENT, (uintptr_t)value); + HndSetHandleExtraInfo(handle, type, (uintptr_t)value); } #ifdef FEATURE_VARIABLE_HANDLES @@ -1240,7 +1282,6 @@ DhContext *Ref_GetDependentHandleContext(ScanContext* sc) bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext) { LOG((LF_GC, LL_INFO10000, "Checking liveness of referents of dependent handles in generation %u\n", pDhContext->m_iCondemned)); - uint32_t type = HNDTYPE_DEPENDENT; uint32_t flags = (pDhContext->m_pScanContext->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL; flags |= HNDGCF_EXTRAINFO; @@ -1281,6 +1322,7 @@ bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext) HHANDLETABLE hTable = pTable[uCPUindex]; if (hTable) { + uint32_t type = HNDTYPE_DEPENDENT; HndScanHandlesForGC(hTable, PromoteDependentHandle, uintptr_t(pDhContext->m_pScanContext), @@ -1289,6 +1331,15 @@ bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext) pDhContext->m_iCondemned, pDhContext->m_iMaxGen, flags ); + type = HNDTYPE_DEPENDENT_DEFER_FINALIZE; + HndScanHandlesForGC(hTable, + PromoteDependentHandleDeferFinalize, + uintptr_t(pDhContext->m_pScanContext), + uintptr_t(pDhContext->m_pfnPromoteFunction), + &type, 1, + pDhContext->m_iCondemned, + pDhContext->m_iMaxGen, + flags ); } } } @@ -1309,7 +1360,11 @@ bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext) void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc) { LOG((LF_GC, LL_INFO10000, "Clearing dead dependent handles in generation %u\n", condemned)); - uint32_t type = HNDTYPE_DEPENDENT; + uint32_t types[] = + { + HNDTYPE_DEPENDENT, + HNDTYPE_DEPENDENT_DEFER_FINALIZE + }; uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL; flags |= HNDGCF_EXTRAINFO; @@ -1330,7 +1385,7 @@ void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, Sc HHANDLETABLE hTable = pTable[uCPUindex]; if (hTable) { - HndScanHandlesForGC(hTable, ClearDependentHandle, uintptr_t(sc), 0, &type, 1, condemned, maxgen, flags ); + HndScanHandlesForGC(hTable, ClearDependentHandle, uintptr_t(sc), 0, types, ARRAY_SIZE(types), condemned, maxgen, flags ); } } } @@ -1343,7 +1398,11 @@ 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) { LOG((LF_GC, LL_INFO10000, "Relocating moved dependent handles in generation %u\n", condemned)); - uint32_t type = HNDTYPE_DEPENDENT; + uint32_t types[] = + { + HNDTYPE_DEPENDENT, + HNDTYPE_DEPENDENT_DEFER_FINALIZE + }; uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL; flags |= HNDGCF_EXTRAINFO; @@ -1364,7 +1423,7 @@ void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, HHANDLETABLE hTable = pTable[uCPUindex]; if (hTable) { - HndScanHandlesForGC(hTable, UpdateDependentHandle, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags ); + HndScanHandlesForGC(hTable, UpdateDependentHandle, uintptr_t(sc), uintptr_t(fn), types, ARRAY_SIZE(types), condemned, maxgen, flags ); } } } @@ -1383,7 +1442,11 @@ void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, WRAPPER_NO_CONTRACT; // set up to scan variable handles with the specified mask and trace function - uint32_t type = HNDTYPE_DEPENDENT; + uint32_t types[] = + { + HNDTYPE_DEPENDENT, + HNDTYPE_DEPENDENT_DEFER_FINALIZE + }; struct DIAG_DEPSCANINFO info = { pfnTrace, lp2 }; HandleTableMap *walk = &g_HandleTableMap; @@ -1397,7 +1460,7 @@ void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex]; if (hTable) HndScanHandlesForGC(hTable, TraceDependentHandle, - lp1, (uintptr_t)&info, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags); + lp1, (uintptr_t)&info, types, ARRAY_SIZE(types), condemned, maxgen, HNDGCF_EXTRAINFO | flags); } } walk = walk->pNext; @@ -1819,6 +1882,7 @@ void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc) #endif HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT, + HNDTYPE_DEPENDENT_DEFER_FINALIZE, }; // verify these handles diff --git a/src/coreclr/gc/objecthandle.h b/src/coreclr/gc/objecthandle.h index d579e8631d6ca6..020d87f8776dc7 100644 --- a/src/coreclr/gc/objecthandle.h +++ b/src/coreclr/gc/objecthandle.h @@ -58,7 +58,7 @@ GC_DAC_VISIBLE OBJECTREF GetDependentHandleSecondary(OBJECTHANDLE handle); #ifndef DACCESS_COMPILE -void SetDependentHandleSecondary(OBJECTHANDLE handle, OBJECTREF secondary); +void SetDependentHandleSecondary(HandleType type, OBJECTHANDLE handle, OBJECTREF secondary); #endif // !DACCESS_COMPILE #ifndef DACCESS_COMPILE diff --git a/src/coreclr/inc/cordebug.idl b/src/coreclr/inc/cordebug.idl index 2e06651c9249aa..17f3f51e7da508 100644 --- a/src/coreclr/inc/cordebug.idl +++ b/src/coreclr/inc/cordebug.idl @@ -2575,6 +2575,7 @@ typedef enum CorGCReferenceType CorHandleStrongAsyncPinned = 1<<7, CorHandleStrongSizedByref = 1<<8, CorHandleWeakNativeCom = 1<<9, + CorHandleStrongDependentDeferFinalize = 1<<10, CorHandleWeakWinRT = CorHandleWeakNativeCom, CorReferenceStack = 0x80000001, diff --git a/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp b/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp index 23e985357d343a..a0e14766442477 100644 --- a/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp @@ -21,9 +21,10 @@ FCIMPL2(OBJECTHANDLE, RhpHandleAlloc, Object *pObject, int type) } FCIMPLEND -FCIMPL2(OBJECTHANDLE, RhpHandleAllocDependent, Object *pPrimary, Object *pSecondary) +FCIMPL3(OBJECTHANDLE, RhpHandleAllocDependent, CLR_BOOL isDeferFinalize, Object *pPrimary, Object *pSecondary) { - return GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateDependentHandle(pPrimary, pSecondary); + HandleType type = isDeferFinalize ? HNDTYPE_DEPENDENT_DEFER_FINALIZE : HNDTYPE_DEPENDENT; + return GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateDependentHandle(type, pPrimary, pSecondary); } FCIMPLEND @@ -47,9 +48,10 @@ FCIMPL2(Object *, RhHandleGetDependent, OBJECTHANDLE handle, Object **ppSecondar } FCIMPLEND -FCIMPL2(void, RhHandleSetDependentSecondary, OBJECTHANDLE handle, Object *pSecondary) +FCIMPL3(void, RhHandleSetDependentSecondary, CLR_BOOL isDeferFinalize, OBJECTHANDLE handle, Object *pSecondary) { - SetDependentHandleSecondary(handle, pSecondary); + HandleType type = isDeferFinalize ? HNDTYPE_DEPENDENT_DEFER_FINALIZE : HNDTYPE_DEPENDENT; + SetDependentHandleSecondary(type, handle, pSecondary); } FCIMPLEND diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs index 4f8e27abbbadfa..1007211e8b3102 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs @@ -5,20 +5,25 @@ namespace System.Runtime { public struct DependentHandle : IDisposable { - private IntPtr _handle; + private const nint IsDeferFinalizeBit = 1; + + private nint _taggedHandle; public DependentHandle(object? target, object? dependent) => - _handle = RuntimeImports.RhHandleAllocDependent(target, dependent); + _taggedHandle = RuntimeImports.RhHandleAllocDependent(false, target, dependent); + + internal DependentHandle(bool isDeferFinalize, object? target, object? dependent) => + _taggedHandle = RuntimeImports.RhHandleAllocDependent(isDeferFinalize, target, dependent) | (isDeferFinalize ? IsDeferFinalizeBit : 0); - public bool IsAllocated => _handle != IntPtr.Zero; + public bool IsAllocated => _taggedHandle != 0; public object? Target { get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; - if ((nint)handle == 0) + if (handle == 0) { ThrowHelper.ThrowInvalidOperationException(); } @@ -27,9 +32,9 @@ public object? Target } set { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; - if ((nint)handle == 0 || value is not null) + if (handle == 0 || value is not null) { ThrowHelper.ThrowInvalidOperationException(); } @@ -43,9 +48,9 @@ public object? Dependent { get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; - if ((nint)handle == 0) + if (handle == 0) { ThrowHelper.ThrowInvalidOperationException(); } @@ -55,14 +60,16 @@ public object? Dependent } set { - IntPtr handle = _handle; + nint taggedHandle = _taggedHandle; - if ((nint)handle == 0) + if (taggedHandle == 0) { ThrowHelper.ThrowInvalidOperationException(); } - RuntimeImports.RhHandleSetDependentSecondary(handle, value); + nint handle = taggedHandle & ~IsDeferFinalizeBit; + bool isDeferFinalize = (taggedHandle & IsDeferFinalizeBit) != 0; + RuntimeImports.RhHandleSetDependentSecondary(isDeferFinalize, handle, value); } } @@ -70,9 +77,9 @@ public object? Dependent { get { - IntPtr handle = _handle; + nint handle = _taggedHandle & ~IsDeferFinalizeBit; - if ((nint)handle == 0) + if (handle == 0) { ThrowHelper.ThrowInvalidOperationException(); } @@ -85,34 +92,35 @@ public object? Dependent internal object? UnsafeGetTarget() { - return RuntimeImports.RhHandleGet(_handle); + return RuntimeImports.RhHandleGet(_taggedHandle & ~IsDeferFinalizeBit); } internal object? UnsafeGetTargetAndDependent(out object? dependent) { - return RuntimeImports.RhHandleGetDependent(_handle, out dependent); + return RuntimeImports.RhHandleGetDependent(_taggedHandle & ~IsDeferFinalizeBit, out dependent); } internal void UnsafeSetTargetToNull() { - RuntimeImports.RhHandleSet(_handle, null); + RuntimeImports.RhHandleSet(_taggedHandle & ~IsDeferFinalizeBit, null); } internal void UnsafeSetDependent(object? dependent) { - RuntimeImports.RhHandleSetDependentSecondary(_handle, dependent); + nint handle = _taggedHandle & ~IsDeferFinalizeBit; + bool isDeferFinalize = (_taggedHandle & IsDeferFinalizeBit) != 0; + RuntimeImports.RhHandleSetDependentSecondary(isDeferFinalize, handle, dependent); } public void Dispose() { // Forces the DependentHandle back to non-allocated state // (if not already there) and frees the handle if needed. - IntPtr handle = _handle; + IntPtr handle = _taggedHandle & ~IsDeferFinalizeBit; - if ((nint)handle != 0) + if (handle != 0) { - _handle = IntPtr.Zero; - + _taggedHandle = 0; RuntimeImports.RhHandleFree(handle); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs index e06ac7457049a9..8fc0fbb6990e41 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs @@ -443,7 +443,7 @@ public ManagedObjectWrapperHolder(ManagedObjectWrapper* wrapper, object wrappedO { _wrapper = wrapper; _wrappedObject = wrappedObject; - _releaser = new ManagedObjectWrapperReleaser(wrapper); + _releaser = new ManagedObjectWrapperReleaser(wrapper, wrappedObject); _wrapper->HolderHandle = RuntimeImports.RhHandleAllocRefCounted(this); } @@ -457,22 +457,19 @@ public ManagedObjectWrapperHolder(ManagedObjectWrapper* wrapper, object wrappedO internal sealed unsafe class ManagedObjectWrapperReleaser { private ManagedObjectWrapper* _wrapper; + private DependentHandle _handle; - public ManagedObjectWrapperReleaser(ManagedObjectWrapper* wrapper) + public ManagedObjectWrapperReleaser(ManagedObjectWrapper* wrapper, object wrappedObject) { _wrapper = wrapper; + _handle = new(true, wrappedObject, this); } ~ManagedObjectWrapperReleaser() { - IntPtr refCountedHandle = _wrapper->HolderHandle; - if (refCountedHandle != IntPtr.Zero && RuntimeImports.RhHandleGet(refCountedHandle) != null) + if (_handle.IsAllocated) { - // The ManagedObjectWrapperHolder has not been fully collected, so it is still - // potentially reachable via the Conditional Weak Table. - // Keep ourselves alive in case the wrapped object is resurrected. - GC.ReRegisterForFinalize(this); - return; + _handle.Dispose(); } // Release GC handle created when MOW was built. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs index ccb969f1728011..63efe0ef6773b7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs @@ -12,7 +12,7 @@ public static unsafe partial class ObjectiveCMarshal { private static readonly IntPtr[] s_ObjcMessageSendFunctions = new IntPtr[(int)MessageSendFunction.MsgSendSuperStret + 1]; private static bool s_initialized; - private static readonly ConditionalWeakTable s_objects = new(); + private static readonly ConditionalWeakTable s_objects = new(isDeferFinalize: true); private static delegate* unmanaged[SuppressGCTransition] s_IsTrackedReferenceCallback; private static delegate* unmanaged[SuppressGCTransition] s_OnEnteredFinalizerQueueCallback; @@ -137,7 +137,6 @@ private static IntPtr CreateReferenceTrackingHandleInternal( } var trackerInfo = s_objects.GetValue(obj, static o => new ObjcTrackingInformation()); - trackerInfo.EnsureInitialized(obj); trackerInfo.GetTaggedMemory(out memInSizeT, out mem); return RuntimeImports.RhHandleAllocRefCounted(obj); } @@ -149,7 +148,6 @@ internal class ObjcTrackingInformation private const int TAGGED_MEMORY_SIZE_IN_POINTERS = 2; internal IntPtr _memory; - private IntPtr _longWeakHandle; public ObjcTrackingInformation() { @@ -162,38 +160,13 @@ public void GetTaggedMemory(out int memInSizeT, out IntPtr mem) mem = _memory; } - public void EnsureInitialized(object o) - { - if (_longWeakHandle != IntPtr.Zero) - { - return; - } - - IntPtr newHandle = RuntimeImports.RhHandleAlloc(o, GCHandleType.WeakTrackResurrection); - if (Interlocked.CompareExchange(ref _longWeakHandle, newHandle, IntPtr.Zero) != IntPtr.Zero) - { - RuntimeImports.RhHandleFree(newHandle); - } - } - ~ObjcTrackingInformation() { - if (_longWeakHandle != IntPtr.Zero && RuntimeImports.RhHandleGet(_longWeakHandle) != null) - { - GC.ReRegisterForFinalize(this); - return; - } - if (_memory != IntPtr.Zero) { NativeMemory.Free((void*)_memory); _memory = IntPtr.Zero; } - if (_longWeakHandle != IntPtr.Zero) - { - RuntimeImports.RhHandleFree(_longWeakHandle); - _longWeakHandle = IntPtr.Zero; - } } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index 0e1ad8d0486534..229646e8d5355c 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -89,7 +89,7 @@ private static ConditionalWeakTable GetPInvokeDe { Interlocked.CompareExchange( ref s_pInvokeDelegates, - new ConditionalWeakTable(), + new ConditionalWeakTable(isDeferFinalize: true), null ); } @@ -151,13 +151,6 @@ public PInvokeDelegateThunk(Delegate del) GCHandle handle = ((ThunkContextData*)ContextData)->Handle; if (handle.IsAllocated) { - // If the delegate is still alive, defer finalization. - if (handle.Target != null) - { - GC.ReRegisterForFinalize(this); - return; - } - handle.Free(); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.NativeAot.cs index 2e2674fb0e1f8b..a42305a00de2de 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.NativeAot.cs @@ -357,11 +357,11 @@ public bool AddDependentHandle(object target, object dependent) if (handle != default) { RuntimeImports.RhHandleSet(handle, target); - RuntimeImports.RhHandleSetDependentSecondary(handle, dependent); + RuntimeImports.RhHandleSetDependentSecondary(false, handle, dependent); } else { - _pHandles[_freeIndex] = RuntimeImports.RhpHandleAllocDependent(target, dependent); + _pHandles[_freeIndex] = RuntimeImports.RhpHandleAllocDependent(false, target, dependent); if (_pHandles[_freeIndex] == default) { return false; @@ -412,7 +412,7 @@ public bool Reset() if (handle != default) { RuntimeImports.RhHandleSet(handle, null); - RuntimeImports.RhHandleSetDependentSecondary(handle, null); + RuntimeImports.RhHandleSetDependentSecondary(false, handle, null); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index a07cbef77c58b6..e612f372710a4b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -291,11 +291,11 @@ internal static IntPtr RhHandleAllocRefCounted(object value) // Allocate handle for dependent handle case where a secondary can be set at the same time. [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpHandleAllocDependent")] - internal static extern IntPtr RhpHandleAllocDependent(object primary, object secondary); + internal static extern IntPtr RhpHandleAllocDependent(bool isDeferFinalize, object primary, object secondary); - internal static IntPtr RhHandleAllocDependent(object primary, object secondary) + internal static IntPtr RhHandleAllocDependent(bool isDeferFinalize, object primary, object secondary) { - IntPtr h = RhpHandleAllocDependent(primary, secondary); + IntPtr h = RhpHandleAllocDependent(isDeferFinalize, primary, secondary); if (h == IntPtr.Zero) throw new OutOfMemoryException(); return h; @@ -334,7 +334,7 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary) // Set the secondary object reference into a dependent handle. [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhHandleSetDependentSecondary")] - internal static extern void RhHandleSetDependentSecondary(IntPtr handle, object secondary); + internal static extern void RhHandleSetDependentSecondary(bool isDeferFinalize, IntPtr handle, object secondary); // // calls to runtime for thunk pool diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs index c75a5fe9de40d1..e3d0508d35cc7d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs @@ -102,7 +102,7 @@ public static unsafe int AssignEntry(object obj, int* pHeader) // Allocate the synchronization object outside the lock Lock lck = new Lock(); DeadEntryCollector collector = new DeadEntryCollector(); - DependentHandle handle = new DependentHandle(obj, collector); + DependentHandle handle = new DependentHandle(true, obj, collector); try { @@ -309,12 +309,7 @@ public DeadEntryCollector() { ref Entry entry = ref s_entries[_index]; - if (entry.Owner.Target != null) - { - // Retry later if the owner is not collected yet. - GC.ReRegisterForFinalize(this); - return; - } + Debug.Assert(entry.Owner.Target is null); dependentHandleToDispose = entry.Owner; entry.Owner = default; diff --git a/src/coreclr/pal/prebuilt/inc/cordebug.h b/src/coreclr/pal/prebuilt/inc/cordebug.h index 6d74b1a412f8c4..048d195e80997e 100644 --- a/src/coreclr/pal/prebuilt/inc/cordebug.h +++ b/src/coreclr/pal/prebuilt/inc/cordebug.h @@ -6707,6 +6707,7 @@ enum CorGCReferenceType CorHandleStrongAsyncPinned = ( 1 << 7 ) , CorHandleStrongSizedByref = ( 1 << 8 ) , CorHandleWeakNativeCom = ( 1 << 9 ) , + CorHandleStrongDependentDeferFinalize = ( 1 << 10 ) , CorHandleWeakWinRT = CorHandleWeakNativeCom, CorReferenceStack = 0x80000001, CorReferenceFinalizer = 80000002, diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 5924e4f4b9fb53..6c03f4af848bed 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -1068,7 +1068,13 @@ class BaseDomain OBJECTHANDLE CreateDependentHandle(OBJECTREF primary, OBJECTREF secondary) { WRAPPER_NO_CONTRACT; - return ::CreateDependentHandle(m_handleStore, primary, secondary); + return ::CreateDependentHandle(false, m_handleStore, primary, secondary); + } + + OBJECTHANDLE CreateDependentHandleDeferFinalize(OBJECTREF primary, OBJECTREF secondary) + { + WRAPPER_NO_CONTRACT; + return ::CreateDependentHandle(true, m_handleStore, primary, secondary); } #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/comdependenthandle.cpp b/src/coreclr/vm/comdependenthandle.cpp index 8f78d01b365bc7..d2f4afb916e6ae 100644 --- a/src/coreclr/vm/comdependenthandle.cpp +++ b/src/coreclr/vm/comdependenthandle.cpp @@ -12,7 +12,7 @@ #include "common.h" #include "comdependenthandle.h" -FCIMPL2(OBJECTHANDLE, DependentHandle::InternalAlloc, Object *target, Object *dependent) +FCIMPL3(OBJECTHANDLE, DependentHandle::InternalAlloc, CLR_BOOL isDeferFinalize, Object *target, Object *dependent) { FCALL_CONTRACT; @@ -20,11 +20,13 @@ FCIMPL2(OBJECTHANDLE, DependentHandle::InternalAlloc, Object *target, Object *de if (CORProfilerTrackGC()) return NULL; - return GetAppDomain()->GetHandleStore()->CreateDependentHandle(target, dependent); + return GetAppDomain()->GetHandleStore()->CreateDependentHandle( + isDeferFinalize ? HNDTYPE_DEPENDENT_DEFER_FINALIZE : HNDTYPE_DEPENDENT, + target, dependent); } FCIMPLEND -extern "C" OBJECTHANDLE QCALLTYPE DependentHandle_InternalAllocWithGCTransition(QCall::ObjectHandleOnStack target, QCall::ObjectHandleOnStack dependent) +extern "C" OBJECTHANDLE QCALLTYPE DependentHandle_InternalAllocWithGCTransition(BOOL isDeferFinalize, QCall::ObjectHandleOnStack target, QCall::ObjectHandleOnStack dependent) { QCALL_CONTRACT; @@ -33,7 +35,9 @@ extern "C" OBJECTHANDLE QCALLTYPE DependentHandle_InternalAllocWithGCTransition( BEGIN_QCALL; GCX_COOP(); - result = GetAppDomain()->CreateDependentHandle(target.Get(), dependent.Get()); + result = isDeferFinalize ? + GetAppDomain()->CreateDependentHandleDeferFinalize(target.Get(), dependent.Get()) : + GetAppDomain()->CreateDependentHandle(target.Get(), dependent.Get()); END_QCALL; @@ -93,18 +97,18 @@ FCIMPL1(VOID, DependentHandle::InternalSetTargetToNull, OBJECTHANDLE handle) } FCIMPLEND -FCIMPL2(VOID, DependentHandle::InternalSetDependent, OBJECTHANDLE handle, Object *_dependent) +FCIMPL3(VOID, DependentHandle::InternalSetDependent, CLR_BOOL isDeferFinalize, OBJECTHANDLE handle, Object *_dependent) { FCALL_CONTRACT; _ASSERTE(handle != NULL); IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - mgr->SetDependentHandleSecondary(handle, _dependent); + mgr->SetDependentHandleSecondary(isDeferFinalize ? HNDTYPE_DEPENDENT_DEFER_FINALIZE : HNDTYPE_DEPENDENT, handle, _dependent); } FCIMPLEND -FCIMPL1(FC_BOOL_RET, DependentHandle::InternalFree, OBJECTHANDLE handle) +FCIMPL2(FC_BOOL_RET, DependentHandle::InternalFree, CLR_BOOL isDeferFinalize, OBJECTHANDLE handle) { FCALL_CONTRACT; @@ -114,12 +118,20 @@ FCIMPL1(FC_BOOL_RET, DependentHandle::InternalFree, OBJECTHANDLE handle) if (CORProfilerTrackGC()) FC_RETURN_BOOL(false); - DestroyDependentHandle(handle); + if (isDeferFinalize) + { + DestroyDependentDeferFinalizeHandle(handle); + } + else + { + DestroyDependentHandle(handle); + } + FC_RETURN_BOOL(true); } FCIMPLEND -extern "C" void QCALLTYPE DependentHandle_InternalFreeWithGCTransition(OBJECTHANDLE handle) +extern "C" void QCALLTYPE DependentHandle_InternalFreeWithGCTransition(BOOL isDeferFinalize, OBJECTHANDLE handle) { QCALL_CONTRACT; @@ -128,7 +140,14 @@ extern "C" void QCALLTYPE DependentHandle_InternalFreeWithGCTransition(OBJECTHAN BEGIN_QCALL; GCX_COOP(); - DestroyDependentHandle(handle); + if (isDeferFinalize) + { + DestroyDependentDeferFinalizeHandle(handle); + } + else + { + DestroyDependentHandle(handle); + } END_QCALL; } diff --git a/src/coreclr/vm/comdependenthandle.h b/src/coreclr/vm/comdependenthandle.h index 62e94161d884ea..19af7661469936 100644 --- a/src/coreclr/vm/comdependenthandle.h +++ b/src/coreclr/vm/comdependenthandle.h @@ -40,16 +40,16 @@ class DependentHandle { public: - static FCDECL2(OBJECTHANDLE, InternalAlloc, Object *target, Object *dependent); + static FCDECL3(OBJECTHANDLE, InternalAlloc, CLR_BOOL isDeferFinalize, Object *target, Object *dependent); static FCDECL1(Object *, InternalGetTarget, OBJECTHANDLE handle); static FCDECL1(Object *, InternalGetDependent, OBJECTHANDLE handle); static FCDECL2(Object *, InternalGetTargetAndDependent, OBJECTHANDLE handle, Object **outDependent); static FCDECL1(VOID, InternalSetTargetToNull, OBJECTHANDLE handle); - static FCDECL2(VOID, InternalSetDependent, OBJECTHANDLE handle, Object *dependent); - static FCDECL1(FC_BOOL_RET, InternalFree, OBJECTHANDLE handle); + static FCDECL3(VOID, InternalSetDependent, CLR_BOOL isDeferFinalize, OBJECTHANDLE handle, Object *dependent); + static FCDECL2(FC_BOOL_RET, InternalFree, CLR_BOOL isDeferFinalize, OBJECTHANDLE handle); }; -extern "C" OBJECTHANDLE QCALLTYPE DependentHandle_InternalAllocWithGCTransition(QCall::ObjectHandleOnStack target, QCall::ObjectHandleOnStack dependent); -extern "C" void QCALLTYPE DependentHandle_InternalFreeWithGCTransition(OBJECTHANDLE handle); +extern "C" OBJECTHANDLE QCALLTYPE DependentHandle_InternalAllocWithGCTransition(BOOL isDeferFinalize, QCall::ObjectHandleOnStack target, QCall::ObjectHandleOnStack dependent); +extern "C" void QCALLTYPE DependentHandle_InternalFreeWithGCTransition(BOOL isDeferFinalize, OBJECTHANDLE handle); #endif diff --git a/src/coreclr/vm/gchandleutilities.h b/src/coreclr/vm/gchandleutilities.h index 1f0e7013f22b61..d6a7bd45a6da55 100644 --- a/src/coreclr/vm/gchandleutilities.h +++ b/src/coreclr/vm/gchandleutilities.h @@ -135,9 +135,9 @@ inline OBJECTHANDLE CreateSizedRefHandle(IGCHandleStore* store, OBJECTREF object return hnd; } -inline OBJECTHANDLE CreateDependentHandle(IGCHandleStore* store, OBJECTREF primary, OBJECTREF secondary) +inline OBJECTHANDLE CreateDependentHandle(bool isDeferFinalize, IGCHandleStore* store, OBJECTREF primary, OBJECTREF secondary) { - OBJECTHANDLE hnd = store->CreateDependentHandle(OBJECTREFToObject(primary), OBJECTREFToObject(secondary)); + OBJECTHANDLE hnd = store->CreateDependentHandle(isDeferFinalize ? HNDTYPE_DEPENDENT_DEFER_FINALIZE : HNDTYPE_DEPENDENT, OBJECTREFToObject(primary), OBJECTREFToObject(secondary)); if (!hnd) { COMPlusThrowOM(); @@ -286,6 +286,11 @@ inline void DestroyDependentHandle(OBJECTHANDLE handle) DestroyHandleCommon(handle, HNDTYPE_DEPENDENT); } +inline void DestroyDependentDeferFinalizeHandle(OBJECTHANDLE handle) +{ + DestroyHandleCommon(handle, HNDTYPE_DEPENDENT_DEFER_FINALIZE); +} + inline void DestroyGlobalHandle(OBJECTHANDLE handle) { DestroyHandleCommon(handle, HNDTYPE_DEFAULT); diff --git a/src/coreclr/vm/rcwrefcache.cpp b/src/coreclr/vm/rcwrefcache.cpp index 7894ff89bd75e0..d582cf1e33d1e4 100644 --- a/src/coreclr/vm/rcwrefcache.cpp +++ b/src/coreclr/vm/rcwrefcache.cpp @@ -174,7 +174,7 @@ void RCWRefCache::ShrinkDependentHandles() IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); mgr->StoreObjectInHandle(depHnd, NULL); - mgr->SetDependentHandleSecondary(depHnd, NULL); + mgr->SetDependentHandleSecondary(HNDTYPE_DEPENDENT, depHnd, NULL); LOG((LF_INTEROP, LL_INFO1000, "\t[RCWRefCache 0x%p] DependentHandle 0x%p cleared @ index %d\n", this, depHnd, (ULONG) i)); } @@ -260,7 +260,7 @@ HRESULT RCWRefCache::AddReferenceUsingDependentHandle(OBJECTREF obj1, OBJECTREF IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); mgr->StoreObjectInHandle(depHnd, OBJECTREFToObject(obj1)); - mgr->SetDependentHandleSecondary(depHnd, OBJECTREFToObject(obj2)); + mgr->SetDependentHandleSecondary(HNDTYPE_DEPENDENT, depHnd, OBJECTREFToObject(obj2)); STRESS_LOG3( LF_INTEROP, LL_INFO1000, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index 916b34ad4df8dd..57789bfde62193 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -31,6 +31,9 @@ public sealed class ConditionalWeakTableGets the value of the specified key. /// key of the value to find. Cannot be null. /// @@ -508,7 +520,11 @@ internal void CreateEntryNoResize(TKey key, TValue value) int newEntry = _firstFreeEntry++; _entries[newEntry].HashCode = hashCode; +#if !MONO + _entries[newEntry].depHnd = new DependentHandle(_parent._isDeferFinalize, key, value); +#else _entries[newEntry].depHnd = new DependentHandle(key, value); +#endif int bucket = hashCode & (_buckets.Length - 1); _entries[newEntry].Next = _buckets[bucket]; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/DependentHandleTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/DependentHandleTests.cs index 3c798557d321cb..0311de34a3e59f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/DependentHandleTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/DependentHandleTests.cs @@ -330,5 +330,95 @@ public void Dispose_ValidOnDefault() Assert.False(handle.IsAllocated); handle.Dispose(); } + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + extern static DependentHandle CreateDependentHandle(bool isDeferFinalize, object primary, object secondary); + + [InlineData(true)] + [InlineData(false)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime))] + public void DeferFinalize(bool isDeferFinalize) + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(bool isDeferFinalize) + { + return CreateDependentHandle(isDeferFinalize, new Primary(), new Secondary()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AssertTargetAndDependentAreNotNull(DependentHandle hand) + { + Assert.NotNull(hand.Target); + Assert.NotNull(hand.Dependent); + + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AssertTargetAndDependentAreNull(DependentHandle hand) + { + Assert.Null(hand.Target); + Assert.Null(hand.Dependent); + } + + Primary.s_finialized = false; + Secondary.s_finialized = false; + Assert.Null(Primary.s_resurrected); + + DependentHandle hand; + try + { + hand = Initialize(isDeferFinalize); + + ForceGC(); + + Assert.True(Primary.s_finialized); + Assert.Equal(!isDeferFinalize, Secondary.s_finialized); + AssertTargetAndDependentAreNotNull(hand); + } + finally + { + Primary.s_resurrected = null; + } + + ForceGC(); + + Assert.True(Primary.s_finialized); + Assert.True(Secondary.s_finialized); + AssertTargetAndDependentAreNull(hand); + + hand.Dispose(); + } + + static void ForceGC() + { + for (int i = 0; i < 5; ++i) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } + + + class Primary + { + internal static bool s_finialized; + internal static Primary? s_resurrected; + + ~Primary() + { + s_resurrected = this; + s_finialized = true; + } + } + + class Secondary + { + internal static bool s_finialized; + + ~Secondary() + { + s_finialized = true; + } + } } }