diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 13d0859831b76..153c1f6c56bc6 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -133,6 +133,7 @@ + @@ -244,8 +245,6 @@ - - diff --git a/src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.cs new file mode 100644 index 0000000000000..de9aeddf40305 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.cs @@ -0,0 +1,57 @@ +// 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.InteropServices; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS +namespace System +{ + internal sealed partial class ComAwareWeakReference + { + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWeakRefToObject")] + private static partial void ComWeakRefToObject(IntPtr pComWeakRef, long wrapperId, ObjectHandleOnStack retRcw); + + internal static object? ComWeakRefToObject(IntPtr pComWeakRef, long wrapperId) + { + object? retRcw = null; + ComWeakRefToObject(pComWeakRef, wrapperId, ObjectHandleOnStack.Create(ref retRcw)); + return retRcw; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe bool PossiblyComObject(object target) + { + // see: syncblk.h + const int IS_HASHCODE_BIT_NUMBER = 26; + const int BIT_SBLK_IS_HASHCODE = 1 << IS_HASHCODE_BIT_NUMBER; + const int BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX = 0x08000000; + + fixed (byte* pRawData = &target.GetRawData()) + { + // The header is 4 bytes before MT field on all architectures + int header = *(int*)(pRawData - sizeof(IntPtr) - sizeof(int)); + // common case: target does not have a syncblock, so there is no interop info + return (header & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE)) == BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX; + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool HasInteropInfo(object target); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjectToComWeakRef")] + private static partial IntPtr ObjectToComWeakRef(ObjectHandleOnStack retRcw, out long wrapperId); + + internal static nint ObjectToComWeakRef(object target, out long wrapperId) + { + if (HasInteropInfo(target)) + { + return ObjectToComWeakRef(ObjectHandleOnStack.Create(ref target), out wrapperId); + } + + wrapperId = 0; + return IntPtr.Zero; + } + } +} +#endif diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 7021d40d06675..a93afa1efd507 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -305,7 +305,7 @@ public static void KeepAlive(object? obj) // public static int GetGeneration(WeakReference wo) { - int result = GetGenerationWR(wo.m_handle); + int result = GetGenerationWR(wo.WeakHandle); KeepAlive(wo); return result; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs index 8597e5c6ed80e..b84be38d886fc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs @@ -8,7 +8,7 @@ namespace System.Runtime.InteropServices public partial struct GCHandle { [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr InternalAlloc(object? value, GCHandleType type); + internal static extern IntPtr InternalAlloc(object? value, GCHandleType type); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void InternalFree(IntPtr handle); diff --git a/src/coreclr/System.Private.CoreLib/src/System/WeakReference.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/WeakReference.CoreCLR.cs deleted file mode 100644 index 0deef24348dd5..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/WeakReference.CoreCLR.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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.Serialization; -using System.Runtime.CompilerServices; -using System.Diagnostics; - -namespace System -{ - public partial class WeakReference : ISerializable - { - // If you fix bugs here, please fix them in WeakReference at the same time. - - // This field is not a regular GC handle. It can have a special values that are used to prevent a race condition between setting the target and finalization. - internal IntPtr m_handle; - - // Determines whether or not this instance of WeakReference still refers to an object - // that has not been collected. - // - public extern virtual bool IsAlive - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - - // Gets the Object stored in the handle if it's accessible. - // Or sets it. - // - public extern virtual object? Target - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - [MethodImpl(MethodImplOptions.InternalCall)] - set; - } - - // Free all system resources associated with this reference. - // - // Note: The WeakReference finalizer is not actually run, but - // treated specially in gc.cpp's ScanForFinalization - // This is needed for subclasses deriving from WeakReference, however. - // Additionally, there may be some cases during shutdown when we run this finalizer. - [MethodImpl(MethodImplOptions.InternalCall)] - extern ~WeakReference(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void Create(object? target, bool trackResurrection); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool IsTrackResurrection(); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/WeakReference.T.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/WeakReference.T.CoreCLR.cs deleted file mode 100644 index dd5f0d900d3a2..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/WeakReference.T.CoreCLR.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; -using System.Runtime.CompilerServices; - -namespace System -{ - public sealed partial class WeakReference : ISerializable - where T : class? - { - // This field is not a regular GC handle. It can have a special values that are used to prevent a race condition between setting the target and finalization. - internal IntPtr m_handle; - - public void SetTarget(T target) - { - this.Target = target; - } - - // This is property for better debugging experience (VS debugger shows values of properties when you hover over the variables) - [MaybeNull] - private extern T Target - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - [MethodImpl(MethodImplOptions.InternalCall)] - set; - } - - // Free all system resources associated with this reference. - // - // Note: The WeakReference finalizer is not usually run, but - // treated specially in gc.cpp's ScanForFinalization - // This is needed for subclasses deriving from WeakReference, however. - // Additionally, there may be some cases during shutdown when we run this finalizer. - [MethodImpl(MethodImplOptions.InternalCall)] - extern ~WeakReference(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void Create(T target, bool trackResurrection); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool IsTrackResurrection(); - } -} diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index bb476fa8145aa..b77e0312e239f 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7858,10 +7858,6 @@ void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, u data.Type = param->Type; if (param->Type == HNDTYPE_DEPENDENT) data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); -#ifdef FEATURE_COMINTEROP - else if (param->Type == HNDTYPE_WEAK_NATIVE_COM) - data.Secondary = HndGetHandleExtraInfo(handle.GetAddr()); -#endif // FEATURE_COMINTEROP else data.Secondary = 0; data.AppDomain = param->AppDomain; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index acf458a39d2be..3ba32fb341e70 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -7601,10 +7601,6 @@ UINT32 DacRefWalker::GetHandleWalkerMask() if ((mHandleMask & CorHandleWeakRefCount) || (mHandleMask & CorHandleStrongRefCount)) result |= (1 << HNDTYPE_REFCOUNTED); #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - if (mHandleMask & CorHandleWeakNativeCom) - result |= (1 << HNDTYPE_WEAK_NATIVE_COM); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS if (mHandleMask & CorHandleStrongDependent) result |= (1 << HNDTYPE_DEPENDENT); @@ -7778,11 +7774,6 @@ void CALLBACK DacHandleWalker::EnumCallbackDac(PTR_UNCHECKED_OBJECTREF handle, u data.i64ExtraData = refCnt; break; #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - case HNDTYPE_WEAK_NATIVE_COM: - data.dwType = (DWORD)CorHandleWeakNativeCom; - break; -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS case HNDTYPE_DEPENDENT: data.dwType = (DWORD)CorHandleStrongDependent; diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 7f48fc6819c82..5e5396632511e 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3216,9 +3216,6 @@ 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_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - HNDTYPE_WEAK_NATIVE_COM -#endif // FEATURE_COMINTEROP }; return GetHandleEnumForTypes(types, ARRAY_SIZE(types), ppHandleEnum); @@ -3257,9 +3254,6 @@ HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppH #if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - HNDTYPE_WEAK_NATIVE_COM -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS }; DacHandleWalker *walker = new DacHandleWalker(); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 5dddd893c39dd..7e8c1b5665fac 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -465,6 +465,10 @@ typedef enum * code holding onto a native weak reference can always access an RCW to the * underlying COM object as long as it has not been released by all of its strong * references. + * + * NOTE: HNDTYPE_WEAK_NATIVE_COM is no longer used in the VM starting .NET 8 + * but we are keeping it here for backward compatibility purposes" + * */ HNDTYPE_WEAK_NATIVE_COM = 9 } HandleType; diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 13642fe1ab6c9..b1d915efb6f7e 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -162,6 +162,9 @@ DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pThreadClass, ::g_pThreadClass) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pPredefinedArrayTypes, ::g_pPredefinedArrayTypes) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_TypedReferenceMT, ::g_TypedReferenceMT) +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceClass, ::g_pWeakReferenceClass) +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceOfTClass, ::g_pWeakReferenceOfTClass) + #ifdef FEATURE_COMINTEROP DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pBaseCOMObject, ::g_pBaseCOMObject) #endif diff --git a/src/coreclr/nativeaot/Runtime/ObjectLayout.h b/src/coreclr/nativeaot/Runtime/ObjectLayout.h index 78ba3a925e825..9aeaf7903da67 100644 --- a/src/coreclr/nativeaot/Runtime/ObjectLayout.h +++ b/src/coreclr/nativeaot/Runtime/ObjectLayout.h @@ -136,5 +136,5 @@ static uintptr_t const MAX_STRING_LENGTH = 0x3FFFFFDF; class WeakReference : public Object { public: - uintptr_t m_HandleAndKind; + uintptr_t m_taggedHandle; }; diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp index ec3f1be198f8e..ead3936f3bdf0 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -1181,10 +1181,15 @@ bool GCToEEInterface::EagerFinalized(Object* obj) // Managed code should not be running. ASSERT(GCHeapUtilities::GetGCHeap()->IsGCInProgressHelper()); + // the lowermost 1 bit is reserved for storing additional info about the handle + const uintptr_t HandleTagBits = 1; + WeakReference* weakRefObj = (WeakReference*)obj; - OBJECTHANDLE handle = (OBJECTHANDLE)(weakRefObj->m_HandleAndKind & ~(uintptr_t)1); - HandleType handleType = (weakRefObj->m_HandleAndKind & 1) ? HandleType::HNDTYPE_WEAK_LONG : HandleType::HNDTYPE_WEAK_SHORT; - weakRefObj->m_HandleAndKind &= (uintptr_t)1; + OBJECTHANDLE handle = (OBJECTHANDLE)(weakRefObj->m_taggedHandle & ~HandleTagBits); + _ASSERTE((weakRefObj->m_taggedHandle & 2) == 0); + HandleType handleType = (weakRefObj->m_taggedHandle & 1) ? HandleType::HNDTYPE_WEAK_LONG : HandleType::HNDTYPE_WEAK_SHORT; + // keep the bit that indicates whether this reference was tracking resurrection, clear the rest. + weakRefObj->m_taggedHandle &= HandleTagBits; GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfType(handle, handleType); return true; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 240f723cbaada..0c0e84b3b7d76 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -169,6 +169,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ComAwareWeakReference.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ComAwareWeakReference.NativeAot.cs new file mode 100644 index 0000000000000..d97196a7e31ca --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ComAwareWeakReference.NativeAot.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS +namespace System +{ + internal sealed partial class ComAwareWeakReference + { + internal static object? ComWeakRefToObject(IntPtr pComWeakRef, long wrapperId) + { + // NativeAOT support for COM WeakReference is NYI + throw new NotImplementedException(); + } + + internal static bool PossiblyComObject(object target) + { + // NativeAOT support for COM WeakReference is NYI + return false; + } + + internal static IntPtr ObjectToComWeakRef(object target, out long wrapperId) + { + // NativeAOT support for COM WeakReference is NYI + wrapperId = 0; + return 0; + } + } +} +#endif diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs index de02ae6eec0e0..7e8441cdb90b8 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs @@ -86,7 +86,9 @@ public static int GetGeneration(WeakReference wo) { // note - this throws an NRE if given a null weak reference. This isn't // documented, but it's the behavior of Desktop and CoreCLR. - object? obj = RuntimeImports.RhHandleGet(wo.Handle); + object? obj = RuntimeImports.RhHandleGet(wo.WeakHandle); + KeepAlive(wo); + if (obj == null) { throw new ArgumentNullException(nameof(wo)); diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 76421d72dfce7..fe4e901de5719 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1384,6 +1384,9 @@ void SystemDomain::LoadBaseSystemClasses() g_pThreadClass = CoreLibBinder::GetClass(CLASS__THREAD); + g_pWeakReferenceClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCE); + g_pWeakReferenceOfTClass = CoreLibBinder::GetClass(CLASS__WEAKREFERENCEGENERIC); + #ifdef FEATURE_COMINTEROP if (g_pConfig->IsBuiltInCOMSupported()) { diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index ecf13313bef38..0dbff5efbf6b7 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -1064,12 +1064,6 @@ class BaseDomain WRAPPER_NO_CONTRACT; return ::CreateRefcountedHandle(m_handleStore, object); } - - OBJECTHANDLE CreateNativeComWeakHandle(OBJECTREF object, NativeComWeakHandleInfo* pComWeakHandleInfo) - { - WRAPPER_NO_CONTRACT; - return ::CreateNativeComWeakHandle(m_handleStore, object, pComWeakHandleInfo); - } #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS OBJECTHANDLE CreateVariableHandle(OBJECTREF object, UINT type) diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 1a7d26e42dc51..9f97a8001cec5 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -939,11 +939,12 @@ DEFINE_METHOD(GC, KEEP_ALIVE, KeepAlive, DEFINE_METHOD(GC, COLLECT, Collect, SM_RetVoid) DEFINE_METHOD(GC, WAIT_FOR_PENDING_FINALIZERS, WaitForPendingFinalizers, SM_RetVoid) -DEFINE_CLASS_U(System, WeakReference, WeakReferenceObject) -DEFINE_FIELD_U(m_handle, WeakReferenceObject, m_Handle) +DEFINE_CLASS_U(System, WeakReference, WeakReferenceObject) +DEFINE_FIELD_U(_taggedHandle, WeakReferenceObject, m_taggedHandle) DEFINE_CLASS(WEAKREFERENCE, System, WeakReference) +DEFINE_CLASS(WEAKREFERENCEGENERIC, System, WeakReference`1) -DEFINE_CLASS_U(Threading, WaitHandle, WaitHandleBase) +DEFINE_CLASS_U(Threading, WaitHandle, WaitHandleBase) DEFINE_FIELD_U(_waitHandle, WaitHandleBase, m_safeHandle) DEFINE_CLASS(DEBUGGER, Diagnostics, Debugger) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 025a6eb43a69e..f31e5256b6955 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -663,27 +663,13 @@ FCFuncStart(gGCHandleFuncs) FCFuncElement("InternalCompareExchange", MarshalNative::GCHandleInternalCompareExchange) FCFuncEnd() - FCFuncStart(gStreamFuncs) FCFuncElement("HasOverriddenBeginEndRead", StreamNative::HasOverriddenBeginEndRead) FCFuncElement("HasOverriddenBeginEndWrite", StreamNative::HasOverriddenBeginEndWrite) FCFuncEnd() -FCFuncStart(gWeakReferenceFuncs) - FCFuncElement("Create", WeakReferenceNative::Create) - FCFuncElement("Finalize", WeakReferenceNative::Finalize) - FCFuncElement("get_Target", WeakReferenceNative::GetTarget) - FCFuncElement("set_Target", WeakReferenceNative::SetTarget) - FCFuncElement("get_IsAlive", WeakReferenceNative::IsAlive) - FCFuncElement("IsTrackResurrection", WeakReferenceNative::IsTrackResurrection) -FCFuncEnd() - -FCFuncStart(gWeakReferenceOfTFuncs) - FCFuncElement("Create", WeakReferenceOfTNative::Create) - FCFuncElement("Finalize", WeakReferenceOfTNative::Finalize) - FCFuncElement("get_Target", WeakReferenceOfTNative::GetTarget) - FCFuncElement("set_Target", WeakReferenceOfTNative::SetTarget) - FCFuncElement("IsTrackResurrection", WeakReferenceOfTNative::IsTrackResurrection) +FCFuncStart(gComAwareWeakReferenceFuncs) + FCFuncElement("HasInteropInfo", ComAwareWeakReferenceNative::HasInteropInfo) FCFuncEnd() #ifdef FEATURE_COMINTEROP @@ -723,6 +709,7 @@ FCClassElement("Array", "System", gArrayFuncs) FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs) FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers) +FCClassElement("ComAwareWeakReference", "System", gComAwareWeakReferenceFuncs) FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs) FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) @@ -786,8 +773,6 @@ FCClassElement("ValueType", "System", gValueTypeFuncs) FCClassElement("Variant", "System", gVariantFuncs) #endif FCClassElement("WaitHandle", "System.Threading", gWaitHandleFuncs) -FCClassElement("WeakReference", "System", gWeakReferenceFuncs) -FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs) #undef FCFuncElement #undef FCFuncElementSig diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 4b9442184d9af..677f67bef5229 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1094,8 +1094,8 @@ void GCToEEInterface::HandleFatalError(unsigned int exitCode) bool GCToEEInterface::EagerFinalized(Object* obj) { MethodTable* pMT = obj->GetGCSafeMethodTable(); - if (pMT == pWeakReferenceMT || - pMT->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT) + if (pMT == g_pWeakReferenceClass || + pMT->HasSameTypeDefAs(g_pWeakReferenceOfTClass)) { FinalizeWeakReference(obj); return true; diff --git a/src/coreclr/vm/gcenv.ee.standalone.cpp b/src/coreclr/vm/gcenv.ee.standalone.cpp index 9ad0c4c380e7a..6a73fca18f2ce 100644 --- a/src/coreclr/vm/gcenv.ee.standalone.cpp +++ b/src/coreclr/vm/gcenv.ee.standalone.cpp @@ -17,12 +17,6 @@ #include "genanalysis.h" #include "eventpipeadapter.h" -// the method table for the WeakReference class -extern MethodTable* pWeakReferenceMT; - -// The canonical method table for WeakReference -extern MethodTable* pWeakReferenceOfTCanonMT; - // Finalizes a weak reference directly. extern void FinalizeWeakReference(Object* obj); diff --git a/src/coreclr/vm/gcenv.ee.static.cpp b/src/coreclr/vm/gcenv.ee.static.cpp index ab8a3f22f8cb8..9648ede32cf9e 100644 --- a/src/coreclr/vm/gcenv.ee.static.cpp +++ b/src/coreclr/vm/gcenv.ee.static.cpp @@ -17,12 +17,6 @@ #include "genanalysis.h" #include "eventpipeadapter.h" -// the method table for the WeakReference class -extern MethodTable* pWeakReferenceMT; - -// The canonical method table for WeakReference -extern MethodTable* pWeakReferenceOfTCanonMT; - // Finalizes a weak reference directly. extern void FinalizeWeakReference(Object* obj); diff --git a/src/coreclr/vm/gchandleutilities.h b/src/coreclr/vm/gchandleutilities.h index 0aa4f1ded95ac..2dc3a1059d6e7 100644 --- a/src/coreclr/vm/gchandleutilities.h +++ b/src/coreclr/vm/gchandleutilities.h @@ -197,29 +197,6 @@ inline OBJECTHANDLE CreateGlobalRefcountedHandle(OBJECTREF object) return CreateGlobalHandleCommon(object, HNDTYPE_REFCOUNTED); } -// Special handle creation convenience functions - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - -struct NativeComWeakHandleInfo -{ - IWeakReference *WeakReference; - INT64 WrapperId; -}; - -inline OBJECTHANDLE CreateNativeComWeakHandle(IGCHandleStore* store, OBJECTREF object, NativeComWeakHandleInfo* pComWeakHandleInfo) -{ - OBJECTHANDLE hnd = store->CreateHandleWithExtraInfo(OBJECTREFToObject(object), HNDTYPE_WEAK_NATIVE_COM, (void*)pComWeakHandleInfo); - if (!hnd) - { - COMPlusThrowOM(); - } - - DiagHandleCreated(hnd, object); - return hnd; -} -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - // Creates a variable-strength handle inline OBJECTHANDLE CreateVariableHandle(IGCHandleStore* store, OBJECTREF object, uint32_t type) { @@ -368,35 +345,6 @@ inline void DestroyTypedHandle(OBJECTHANDLE handle) GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfUnknownType(handle); } -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) -inline void DestroyNativeComWeakHandle(OBJECTHANDLE handle) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - CAN_TAKE_LOCK; - } - CONTRACTL_END; - - // Delete the COM info and release the weak reference if we have one. We're assuming that - // this will not reenter the runtime, since if we are pointing at a managed object, we should - // not be using HNDTYPE_WEAK_NATIVE_COM but rather HNDTYPE_WEAK_SHORT or HNDTYPE_WEAK_LONG. - void* pExtraInfo = GCHandleUtilities::GetGCHandleManager()->GetExtraInfoFromHandle(handle); - NativeComWeakHandleInfo* comWeakHandleInfo = reinterpret_cast(pExtraInfo); - if (comWeakHandleInfo != nullptr) - { - _ASSERTE(comWeakHandleInfo->WeakReference != nullptr); - comWeakHandleInfo->WeakReference->Release(); - delete comWeakHandleInfo; - } - - DiagHandleDestroyed(handle); - GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfType(handle, HNDTYPE_WEAK_NATIVE_COM); -} -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - // Handle holders/wrappers #ifndef FEATURE_NATIVEAOT diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index 2e245082e6629..dadb391fc225e 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -465,7 +465,7 @@ FCIMPL2(LPVOID, MarshalNative::GCHandleInternalAlloc, Object *obj, int type) OBJECTREF objRef(obj); - assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM); + assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_SIZEDREF); if (CORProfilerTrackGC()) { diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 1b7f140aae89f..d608fe0493a55 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1546,7 +1546,7 @@ class AssemblyNameBaseObject : public Object class WeakReferenceObject : public Object { public: - Volatile m_Handle; + uintptr_t m_taggedHandle; }; #ifdef USE_CHECKED_OBJECTREFS @@ -1567,8 +1567,6 @@ typedef REF ASSEMBLYLOADCONTEXTREF; typedef REF ASSEMBLYNAMEREF; -typedef REF WEAKREFERENCEREF; - inline ARG_SLOT ObjToArgSlot(OBJECTREF objRef) { LIMITED_METHOD_CONTRACT; @@ -1612,10 +1610,6 @@ typedef PTR_AssemblyBaseObject ASSEMBLYREF; typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; -#ifndef DACCESS_COMPILE -typedef WeakReferenceObject* WEAKREFERENCEREF; -#endif // #ifndef DACCESS_COMPILE - #define ObjToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) #define ArgSlotToObj(s) ((OBJECTREF)(SIZE_T)(s)) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index c75b1889226e8..db93c2f3c596a 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -318,6 +318,10 @@ static const Entry s_QCall[] = #if defined(FEATURE_COMINTEROP) DllImportEntry(InterfaceMarshaler__ClearNative) #endif +#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) + DllImportEntry(ComWeakRefToObject) + DllImportEntry(ObjectToComWeakRef) +#endif }; const void* QCallResolveDllImport(const char* name) diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index bc22a1890f9f9..239b7ee006034 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -983,7 +983,7 @@ extern "C" PVOID QCALLTYPE QCall_GetGCHandleForTypeHandle(QCall::TypeHandle pTyp GCX_COOP(); TypeHandle th = pTypeHandle.AsTypeHandle(); - assert(handleType >= HNDTYPE_WEAK_SHORT && handleType <= HNDTYPE_WEAK_NATIVE_COM); + assert(handleType >= HNDTYPE_WEAK_SHORT && handleType <= HNDTYPE_SIZEDREF); objHandle = AppDomain::GetCurrentDomain()->CreateTypedHandle(NULL, static_cast(handleType)); th.GetLoaderAllocator()->RegisterHandleForCleanup(objHandle); diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index edeff0074da90..85737057d2f9c 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -71,6 +71,9 @@ GPTR_IMPL(MethodTable, g_pFreeObjectMethodTable); GPTR_IMPL(MethodTable, g_TypedReferenceMT); +GPTR_IMPL(MethodTable, g_pWeakReferenceClass); +GPTR_IMPL(MethodTable, g_pWeakReferenceOfTClass); + #ifdef FEATURE_COMINTEROP GPTR_IMPL(MethodTable, g_pBaseCOMObject); #endif diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index ccccb411fc2ef..dd92ee7b12fde 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -360,7 +360,6 @@ GPTR_DECL(MethodTable, g_pThreadAbortExceptionClass); GPTR_DECL(MethodTable, g_pOutOfMemoryExceptionClass); GPTR_DECL(MethodTable, g_pStackOverflowExceptionClass); GPTR_DECL(MethodTable, g_pExecutionEngineExceptionClass); -GPTR_DECL(MethodTable, g_pThreadAbortExceptionClass); GPTR_DECL(MethodTable, g_pDelegateClass); GPTR_DECL(MethodTable, g_pMulticastDelegateClass); GPTR_DECL(MethodTable, g_pFreeObjectMethodTable); @@ -370,6 +369,9 @@ GPTR_DECL(MethodTable, g_pThreadClass); GPTR_DECL(MethodTable, g_TypedReferenceMT); +GPTR_DECL(MethodTable, g_pWeakReferenceClass); +GPTR_DECL(MethodTable, g_pWeakReferenceOfTClass); + #ifdef FEATURE_COMINTEROP GPTR_DECL(MethodTable, g_pBaseCOMObject); #endif diff --git a/src/coreclr/vm/weakreferencenative.cpp b/src/coreclr/vm/weakreferencenative.cpp index 946ce8d6675db..f9a7b9a6bfc01 100644 --- a/src/coreclr/vm/weakreferencenative.cpp +++ b/src/coreclr/vm/weakreferencenative.cpp @@ -10,1031 +10,171 @@ #include "common.h" -#include "gchandleutilities.h" #include "weakreferencenative.h" -#include "typestring.h" -#include "threadsuspend.h" #include "interoplibinterface.h" -//************************************************************************ - -// We use several special values of the handle to track extra state without increasing the instance size. -const LPVOID specialWeakReferenceHandles[3] = { 0, 0, 0 }; - -// SPECIAL_HANDLE_SPINLOCK is used to implement spinlock that protects against races between setting the target and finalization -#define SPECIAL_HANDLE_SPINLOCK ((OBJECTHANDLE)(&specialWeakReferenceHandles[0])) - -// SPECIAL_HANDLE_FINALIZED is used to track the original type of the handle so that IsTrackResurrection keeps working on finalized -// objects for backward compatibility. -#define SPECIAL_HANDLE_FINALIZED_SHORT ((OBJECTHANDLE)(&specialWeakReferenceHandles[1])) -#define SPECIAL_HANDLE_FINALIZED_LONG ((OBJECTHANDLE)(&specialWeakReferenceHandles[2])) - -#define IS_SPECIAL_HANDLE(h) ((size_t)(h) - (size_t)(&specialWeakReferenceHandles) < sizeof(specialWeakReferenceHandles)) - -// -// A WeakReference instance can hold one of three types of handles - short or long weak handles, -// or a native COM weak reference handle. The native COM weak reference handle has the extra capability -// of recreating an RCW for a COM object which is still alive even though the previous RCW had -// been collected. In order to differentiate this type of handle from the standard weak handles, -// the bottom bit is stolen. -// -// Note that the bit is stolen only in the local copy of the object handle, held in the m_handle -// field of the weak reference object. The handle in the handle table itself does not have its -// bottom bit stolen, and requires using HandleFetchType to determine what type it is. The bit -// is strictly a performance optimization for the weak reference implementation, and it is -// responsible for setting up the bit as it needs and ensuring that it is cleared whenever an -// object handle leaves the weak reference code, for instance to interact with the handle table -// or diagnostics tools. -// -// The following functions are to set, test, and unset that bit before the handle is used. -// - -// Determine if an object handle is a native COM weak reference handle -bool IsNativeComWeakReferenceHandle(OBJECTHANDLE handle) -{ - STATIC_CONTRACT_LEAF; - return (reinterpret_cast(handle) & 0x1) != 0x0; -} - -// Mark an object handle as being a native COM weak reference handle -OBJECTHANDLE SetNativeComWeakReferenceHandle(OBJECTHANDLE handle) -{ - STATIC_CONTRACT_LEAF; - - _ASSERTE(!IsNativeComWeakReferenceHandle(handle)); - return reinterpret_cast(reinterpret_cast(handle) | 0x1); -} - -// Get the object handle value even if the object is a native COM weak reference -OBJECTHANDLE GetHandleValue(OBJECTHANDLE handle) -{ - STATIC_CONTRACT_LEAF; - UINT_PTR mask = ~(static_cast(0x1)); - return reinterpret_cast(reinterpret_cast(handle) & mask); -} - -FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis); -FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle); - -struct WeakHandleSpinLockHolder -{ - OBJECTHANDLE RawHandle; - OBJECTHANDLE Handle; - WEAKREFERENCEREF* pWeakReference; - - WeakHandleSpinLockHolder(OBJECTHANDLE rawHandle, WEAKREFERENCEREF* weakReference) - : RawHandle(rawHandle), Handle(GetHandleValue(rawHandle)), pWeakReference(weakReference) - { - STATIC_CONTRACT_LEAF; - } - - ~WeakHandleSpinLockHolder() - { - WRAPPER_NO_CONTRACT; - ReleaseWeakHandleSpinLock(*pWeakReference, RawHandle); - } - -private: - WeakHandleSpinLockHolder(const WeakHandleSpinLockHolder& other); - WeakHandleSpinLockHolder& operator=(const WeakHandleSpinLockHolder& other); -}; - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - -// Get the native COM information for the object underlying an RCW if applicable. If the incoming object cannot -// use a native COM weak reference, nullptr is returned. Otherwise, a new NativeComWeakHandleInfo containing an -// AddRef-ed IWeakReference* for the COM object underlying the RCW is returned. -// -// In order to qualify to be used with a HNDTYPE_WEAK_NATIVE_COM, the incoming object must: -// * be an RCW -// * not be an aggregated RCW -// * respond to a QI for IWeakReferenceSource -// * succeed when asked for an IWeakReference* -// -// Note that *pObject should be GC protected on the way into this method -NativeComWeakHandleInfo* GetComWeakReferenceInfo(OBJECTREF* pObject) +// This entrypoint is used for eager finalization by the GC. +void FinalizeWeakReference(Object* obj) { CONTRACTL { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pObject)); + NOTHROW; + GC_NOTRIGGER; } CONTRACTL_END; - if (*pObject == NULL) - { - return nullptr; - } - - ASSERT_PROTECTED(pObject); - - MethodTable* pMT = (*pObject)->GetMethodTable(); - - SafeComHolder pWeakReferenceSource(nullptr); - INT64 wrapperId = ComWrappersNative::InvalidWrapperId; - - // If the object is not an RCW, then we do not want to use a native COM weak reference to it - // If the object is a managed type deriving from a COM type, then we also do not want to use a native COM - // weak reference to it. (Otherwise, we'll wind up resolving IWeakReference-s back into the CLR - // when we don't want to have reentrancy). -#ifdef FEATURE_COMINTEROP - if (pMT->IsComObjectType() - && (pMT == g_pBaseCOMObject || !pMT->IsExtensibleRCW())) - { - pWeakReferenceSource = reinterpret_cast(GetComIPFromObjectRef(pObject, IID_IWeakReferenceSource, false /* throwIfNoComIP */)); - } - else -#endif - { -#ifdef FEATURE_COMWRAPPERS - bool isAggregated = false; - pWeakReferenceSource = reinterpret_cast(ComWrappersNative::GetIdentityForObject(pObject, IID_IWeakReferenceSource, &wrapperId, &isAggregated)); - if (isAggregated) - { - // If the RCW is an aggregated RCW, then the managed object cannot be recreated from the IUnknown as the outer IUnknown wraps the managed object. - // In this case, don't create a weak reference backed by a COM weak reference. - pWeakReferenceSource = nullptr; - } -#endif - } + // Eager finalization happens while scanning for unmarked finalizable objects + // after marking strongly reachable and prior to marking dependent and long weak handles. + // Managed code should not be running. + _ASSERTE(GCHeapUtilities::IsGCInProgress()); - if (pWeakReferenceSource == nullptr) - { - return nullptr; - } + // the lowermost 2 bits are reserved for storing additional info about the handle + // we can use these bits because handle is at least 4 byte aligned + const uintptr_t HandleTagBits = 3; - GCX_PREEMP(); - SafeComHolderPreemp pWeakReference; - if (FAILED(pWeakReferenceSource->GetWeakReference(&pWeakReference))) - { - return nullptr; - } + WeakReferenceObject* weakRefObj = (WeakReferenceObject*)obj; + OBJECTHANDLE handle = (OBJECTHANDLE)(weakRefObj->m_taggedHandle & ~HandleTagBits); + HandleType handleType = (weakRefObj->m_taggedHandle & 2) ? + HandleType::HNDTYPE_STRONG : + (weakRefObj->m_taggedHandle & 1) ? + HandleType::HNDTYPE_WEAK_LONG : + HandleType::HNDTYPE_WEAK_SHORT; - NewHolder info = new NativeComWeakHandleInfo { pWeakReference.GetValue(), wrapperId }; - pWeakReference.SuppressRelease(); - return info.Extract(); + // keep the bit that indicates whether this reference was tracking resurrection, clear the rest. + weakRefObj->m_taggedHandle &= (uintptr_t)1; + GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfType(handle, handleType); } -// Given an object handle that stores a native COM weak reference, attempt to create an RCW -// and store it back in the handle, returning the RCW. If the underlying native COM object -// is not alive, then the result is NULL. -// -// In order to create a new RCW, we must: -// * Have an m_handle of HNDTYPE_WEAK_NATIVE_COM (ie the bottom bit of m_handle is set) -// * Have stored an IWeakReference* in the handle extra info when setting up the handle -// (see GetComWeakReference) -// * The IWeakReference* must respond to a Resolve request for IID_IInspectable -// * -NOINLINE Object* LoadComWeakReferenceTarget(WEAKREFERENCEREF weakReference, TypeHandle targetType, LPVOID __me) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(weakReference != NULL); - } - CONTRACTL_END; - - struct - { - WEAKREFERENCEREF weakReference; - OBJECTREF rcw; - OBJECTREF target; - } gc; - gc.weakReference = weakReference; - gc.rcw = NULL; - gc.target = NULL; - - FC_INNER_PROLOG_NO_ME_SETUP(); - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_PROTECT(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, gc); - - // Acquire the spin lock to get the IWeakReference* associated with the weak reference. We will then need to - // release the lock while resolving the IWeakReference* since we need to enter preemptive mode while calling out - // to COM to resolve the object and we don't want to do that while holding the lock. If we wind up being able - // to geenrate a new RCW, we'll reacquire the lock to save the RCW in the handle. - // - // Since we're acquiring and releasing the lock multiple times, we need to check the handle state each time we - // reacquire the lock to make sure that another thread hasn't reassigned the target of the handle or finalized it - SafeComHolder pComWeakReference = nullptr; - INT64 wrapperId = ComWrappersNative::InvalidWrapperId; - { - WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); - GCX_NOTRIGGER(); +#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - // Make sure that while we were not holding the spin lock, another thread did not change the target of - // this weak reference. Only fetch the IWeakReference* if we still have a valid handle holding a NULL object - // and the handle is still a HNDTYPE_WEAK_NATIVE_COM type handle. - if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) - { - if (*(Object **)(handle.Handle) != NULL) - { - // While we released the spin lock, another thread already set a new target for the weak reference. - // We don't want to replace it with an RCW that we fetch, so save it to return as the object the - // weak reference is targeting. - gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); - } - else if(IsNativeComWeakReferenceHandle(handle.RawHandle)) - { - _ASSERTE(GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handle.Handle) == HNDTYPE_WEAK_NATIVE_COM); +// static +extern "C" void QCALLTYPE ComWeakRefToObject(IWeakReference* pComWeakReference, INT64 wrapperId, QCall::ObjectHandleOnStack retRcw) +{ + QCALL_CONTRACT; + BEGIN_QCALL; - // Retrieve the associated IWeakReference* for this weak reference. Add a reference to it while we release - // the spin lock so that another thread doesn't release it out from underneath us. - // - // Setting pComWeakReference will claim that it triggers a GC, however that's not true in this case because - // it's always set to NULL here and there's nothing for it to release. - _ASSERTE(pComWeakReference.IsNull()); - CONTRACT_VIOLATION(GCViolation); - IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - NativeComWeakHandleInfo* comWeakHandleInfo = reinterpret_cast(mgr->GetExtraInfoFromHandle(handle.Handle)); - if (comWeakHandleInfo != nullptr) - { - wrapperId = comWeakHandleInfo->WrapperId; - pComWeakReference = comWeakHandleInfo->WeakReference; - pComWeakReference->AddRef(); - } - } - } - } + _ASSERTE(pComWeakReference != nullptr); // If the weak reference was in a state that it had an IWeakReference* for us to use, then we need to find the IUnknown - // identity of the underlying COM object (assuming that object is still alive). This work is done without holding the - // spin lock since it will call out to arbitrary code and as such we need to switch to preemptive mode. + // identity of the underlying COM object (assuming that object is still alive). SafeComHolder pTargetIdentity = nullptr; - if (pComWeakReference != nullptr) - { - _ASSERTE(gc.target == NULL); - - GCX_PREEMP(); - // Using the IWeakReference*, get ahold of the target native COM object's IInspectable*. If this resolve fails, then we - // assume that the underlying native COM object is no longer alive, and thus we cannot create a new RCW for it. - SafeComHolderPreemp pTarget = nullptr; - if (SUCCEEDED(pComWeakReference->Resolve(IID_IInspectable, &pTarget))) + // Using the IWeakReference*, get ahold of the target native COM object's IInspectable*. If this resolve fails, then we + // assume that the underlying native COM object is no longer alive, and thus we cannot create a new RCW for it. + SafeComHolderPreemp pTarget = nullptr; + if (SUCCEEDED(pComWeakReference->Resolve(IID_IInspectable, &pTarget))) + { + if (!pTarget.IsNull()) { - if (!pTarget.IsNull()) - { - // Get the IUnknown identity for the underlying object - SafeQueryInterfacePreemp(pTarget, IID_IUnknown, &pTargetIdentity); - } + // Get the IUnknown identity for the underlying object + SafeQueryInterfacePreemp(pTarget, IID_IUnknown, &pTargetIdentity); } } // If we were able to get an IUnknown identity for the object, then we can find or create an associated RCW for it. if (!pTargetIdentity.IsNull()) { + GCX_COOP(); + OBJECTREF rcwRef = NULL; + GCPROTECT_BEGIN(rcwRef); + if (wrapperId != ComWrappersNative::InvalidWrapperId) { // Try the global COM wrappers if (GlobalComWrappersForTrackerSupport::IsRegisteredInstance(wrapperId)) { - (void)GlobalComWrappersForTrackerSupport::TryGetOrCreateObjectForComInstance(pTargetIdentity, &gc.rcw); + (void)GlobalComWrappersForTrackerSupport::TryGetOrCreateObjectForComInstance(pTargetIdentity, &rcwRef); } else if (GlobalComWrappersForMarshalling::IsRegisteredInstance(wrapperId)) { - (void)GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(pTargetIdentity, ObjFromComIP::NONE, &gc.rcw); + (void)GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(pTargetIdentity, ObjFromComIP::NONE, &rcwRef); } } #ifdef FEATURE_COMINTEROP else { // If the original RCW was not created through ComWrappers, fall back to the built-in system. - GetObjectRefFromComIP(&gc.rcw, pTargetIdentity); + GetObjectRefFromComIP(&rcwRef, pTargetIdentity); } #endif // FEATURE_COMINTEROP + GCPROTECT_END(); + retRcw.Set(rcwRef); } - // If we were able to get an RCW, then we need to reacquire the spin lock and store the RCW in the handle. Note that - // it's possible that another thread has acquired the spin lock and set the target of the weak reference while we were - // building the RCW. In that case, we will defer to the hadle that the other thread set, and let the RCW die. - if (gc.rcw != NULL) - { - // Make sure the type we got back from the native COM object is compatible with the type the managed - // weak reference expects. (For instance, in the WeakReference case, the returned type - // had better be compatible with T). - TypeHandle rcwType(gc.rcw->GetMethodTable()); - if (!rcwType.CanCastTo(targetType)) - { - SString weakReferenceTypeName; - TypeString::AppendType(weakReferenceTypeName, targetType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); - - SString resolvedTypeName; - TypeString::AppendType(resolvedTypeName, rcwType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); - - COMPlusThrow(kInvalidCastException, IDS_EE_NATIVE_COM_WEAKREF_BAD_TYPE, weakReferenceTypeName.GetUnicode(), resolvedTypeName.GetUnicode()); - } - - WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); - GCX_NOTRIGGER(); - - - // Now that we've reacquired the lock, see if the handle is still empty. If so, then save the RCW as the new target of the handle. - if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) - { - _ASSERTE(gc.target == NULL); - gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); - - if (gc.target == NULL) - { - StoreObjectInHandle(handle.Handle, gc.rcw); - gc.target = gc.rcw; - } - } - } - - HELPER_METHOD_FRAME_END(); - FC_INNER_EPILOG(); - - return OBJECTREFToObject(gc.target); + END_QCALL; + return; } -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - -//************************************************************************ - -// -// Spinlock implemented by overloading the WeakReference::m_Handle field that protects against races between setting -// the target and finalization -// - -NOINLINE OBJECTHANDLE AcquireWeakHandleSpinLockSpin(WEAKREFERENCEREF pThis) +// static +extern "C" IWeakReference * QCALLTYPE ObjectToComWeakRef(QCall::ObjectHandleOnStack obj, INT64* pWrapperId) { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; + QCALL_CONTRACT; - DWORD dwSwitchCount = 0; - YieldProcessorNormalizationInfo normalizationInfo; + IWeakReference* pWeakReference = nullptr; + BEGIN_QCALL; - // - // Boilerplate spinning logic stolen from other locks - // - for (;;) - { - if (g_SystemInfo.dwNumberOfProcessors > 1) - { - DWORD spincount = g_SpinConstants.dwInitialDuration; - - for (;;) - { - YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, spincount); - - OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); - if (handle != SPECIAL_HANDLE_SPINLOCK) - return handle; - - spincount *= g_SpinConstants.dwBackoffFactor; - if (spincount > g_SpinConstants.dwMaximumDuration) - { - break; - } - } - } - - __SwitchToThread(0, ++dwSwitchCount); - - OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); - if (handle != SPECIAL_HANDLE_SPINLOCK) - return handle; - } -} - -FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); - if (handle != SPECIAL_HANDLE_SPINLOCK) - return handle; - return AcquireWeakHandleSpinLockSpin(pThis); -} - -FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle) -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(newHandle != SPECIAL_HANDLE_SPINLOCK); - pThis->m_Handle = newHandle; -} - -//************************************************************************ - -MethodTable *pWeakReferenceMT = NULL; -MethodTable *pWeakReferenceOfTCanonMT = NULL; - -//************************************************************************ - -FCIMPL3(void, WeakReferenceNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) -{ - FCALL_CONTRACT; - - struct - { - WEAKREFERENCEREF pThis; - OBJECTREF pTarget; - } gc; - - gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); - gc.pTarget = OBJECTREF(pTargetUNSAFE); - - HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); - - if (gc.pThis == NULL) - COMPlusThrow(kNullReferenceException); - - if (pWeakReferenceMT == NULL) - pWeakReferenceMT = CoreLibBinder::GetClass(CLASS__WEAKREFERENCE); - - _ASSERTE(gc.pThis->GetMethodTable()->CanCastToClass(pWeakReferenceMT)); - - // Create the handle. -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - NativeComWeakHandleInfo *comWeakHandleInfo = nullptr; - if (gc.pTarget != NULL) - { - SyncBlock *pSyncBlock = gc.pTarget->PassiveGetSyncBlock(); - if (pSyncBlock != nullptr && pSyncBlock->GetInteropInfoNoCreate() != nullptr) - { - comWeakHandleInfo = GetComWeakReferenceInfo(&gc.pTarget); - } - } - - if (comWeakHandleInfo != nullptr) - { - NewHolder infoHolder(comWeakHandleInfo); - gc.pThis->m_Handle = SetNativeComWeakReferenceHandle(GetAppDomain()->CreateNativeComWeakHandle(gc.pTarget, infoHolder)); - infoHolder.SuppressRelease(); - } - else -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - { - gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, - trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); - } - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3(void, WeakReferenceOfTNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) -{ - FCALL_CONTRACT; - - struct - { - WEAKREFERENCEREF pThis; - OBJECTREF pTarget; - } gc; - - gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); - gc.pTarget = OBJECTREF(pTargetUNSAFE); - - HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); - - if (gc.pThis == NULL) - COMPlusThrow(kNullReferenceException); - - if (pWeakReferenceOfTCanonMT == NULL) - pWeakReferenceOfTCanonMT = gc.pThis->GetMethodTable()->GetCanonicalMethodTable(); - - _ASSERTE(gc.pThis->GetMethodTable()->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT); - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - NativeComWeakHandleInfo *comWeakHandleInfo = nullptr; - if (gc.pTarget != NULL) - { - SyncBlock *pSyncBlock = gc.pTarget->PassiveGetSyncBlock(); - if (pSyncBlock != nullptr && pSyncBlock->GetInteropInfoNoCreate() != nullptr) - { - comWeakHandleInfo = GetComWeakReferenceInfo(&gc.pTarget); - } - } - - if (comWeakHandleInfo != nullptr) - { - NewHolder infoHolder(comWeakHandleInfo); - gc.pThis->m_Handle = SetNativeComWeakReferenceHandle(GetAppDomain()->CreateNativeComWeakHandle(gc.pTarget, infoHolder)); - infoHolder.SuppressRelease(); - } - else -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - { - gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, - trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); - } - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -//************************************************************************ - -// This entrypoint is also used for direct finalization by the GC. Note that we cannot depend on the runtime being suspended -// when this is called because of background GC. Background GC is going to call this method while user managed code is running. -void FinalizeWeakReference(Object * obj) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - WEAKREFERENCEREF pThis((WeakReferenceObject *)(obj)); - - // The suspension state of the runtime must be prevented from changing while in this function in order for this to be safe. - OBJECTHANDLE handle = ThreadSuspend::SysIsSuspended() ? pThis->m_Handle.LoadWithoutBarrier() : AcquireWeakHandleSpinLock(pThis); - OBJECTHANDLE handleToDestroy = NULL; - bool isWeakNativeComHandle = false; + *pWrapperId = ComWrappersNative::InvalidWrapperId; + SafeComHolder pWeakReferenceSource(nullptr); + _ASSERTE(obj.m_ppObject != nullptr); - // Check for not yet constructed or already finalized handle - if ((handle != NULL) && !IS_SPECIAL_HANDLE(handle)) { - handleToDestroy = GetHandleValue(handle); + // COM helpers assume COOP mode and the arguments are protected refs. + GCX_COOP(); + OBJECTREF objRef = obj.Get(); + GCPROTECT_BEGIN(objRef); - // Cache the old handle value - HandleType handleType = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handleToDestroy); -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT || handleType == HNDTYPE_WEAK_NATIVE_COM); - isWeakNativeComHandle = handleType == HNDTYPE_WEAK_NATIVE_COM; -#else // !FEATURE_COMINTEROP && !FEATURE_COMWRAPPERS - _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - - handle = (handleType == HNDTYPE_WEAK_LONG) ? - SPECIAL_HANDLE_FINALIZED_LONG : SPECIAL_HANDLE_FINALIZED_SHORT; - } - - // Release the spin lock - // This is necessary even when the spin lock is not acquired - // (i.e. When ThreadSuspend::SysIsSuspended() == true) - // so that the new handle value is set. - ReleaseWeakHandleSpinLock(pThis, handle); - - if (handleToDestroy != NULL) - { -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - if (isWeakNativeComHandle) + // If the object is not an RCW, then we do not want to use a native COM weak reference to it + // If the object is a managed type deriving from a COM type, then we also do not want to use a native COM + // weak reference to it. (Otherwise, we'll wind up resolving IWeakReference-s back into the CLR + // when we don't want to have reentrancy). +#ifdef FEATURE_COMINTEROP + MethodTable* pMT = objRef->GetMethodTable(); + if (pMT->IsComObjectType() + && (pMT == g_pBaseCOMObject || !pMT->IsExtensibleRCW())) { - DestroyNativeComWeakHandle(handleToDestroy); + pWeakReferenceSource = reinterpret_cast(GetComIPFromObjectRef(&objRef, IID_IWeakReferenceSource, false /* throwIfNoComIP */)); } else -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - { - DestroyTypedHandle(handleToDestroy); - } - } -} - -FCIMPL1(void, WeakReferenceNative::Finalize, WeakReferenceObject * pThis) -{ - FCALL_CONTRACT; - - HELPER_METHOD_FRAME_BEGIN_NOPOLL(); - - if (pThis == NULL) - { - FCUnique(0x1); - COMPlusThrow(kNullReferenceException); - } - - FinalizeWeakReference(pThis); - - HELPER_METHOD_FRAME_END_POLL(); -} -FCIMPLEND - -FCIMPL1(void, WeakReferenceOfTNative::Finalize, WeakReferenceObject * pThis) -{ - FCALL_CONTRACT; - - HELPER_METHOD_FRAME_BEGIN_NOPOLL(); - - if (pThis == NULL) - COMPlusThrow(kNullReferenceException); - - FinalizeWeakReference(pThis); - - HELPER_METHOD_FRAME_END_POLL(); -} -FCIMPLEND - -//************************************************************************ - -#include - -static FORCEINLINE OBJECTREF GetWeakReferenceTarget(WEAKREFERENCEREF pThis) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - OBJECTHANDLE rawHandle = pThis->m_Handle.LoadWithoutBarrier(); - OBJECTHANDLE handle = GetHandleValue(rawHandle); - - if (handle == NULL) - return NULL; - - // Try a speculative lock-free read first - if (rawHandle != SPECIAL_HANDLE_SPINLOCK) - { - // - // There is a theoretic chance that the speculative lock-free read may AV while reading the value - // of freed handle if the handle table decides to release the memory that the handle lives in. - // It is not exploitable security issue because of we will fail fast on the AV. It is denial of service only. - // Non-malicious user code will never hit. - // - // We had this theoretical bug in there since forever. Fixing it by always taking the lock would - // degrade the performance critical weak handle getter several times. The right fix may be - // to ensure that handle table memory is released only if the runtime is suspended. - // - Object * pSpeculativeTarget = VolatileLoad((Object **)(handle)); - - // - // We want to ensure that the handle was still alive when we fetched the target, - // so we double check m_handle here. Note that the reading of the handle - // value has to take memory barrier for this to work, but reading of m_handle does not. - // - if (rawHandle == pThis->m_Handle.LoadWithoutBarrier()) - { - return OBJECTREF(pSpeculativeTarget); - } - } - - - rawHandle = AcquireWeakHandleSpinLock(pThis); - GCX_NOTRIGGER(); - - handle = GetHandleValue(rawHandle); - OBJECTREF pTarget = OBJECTREF(*(Object **)(handle)); - - ReleaseWeakHandleSpinLock(pThis, rawHandle); - - return pTarget; -} - -FCIMPL1(Object *, WeakReferenceNative::GetTarget, WeakReferenceObject * pThisUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - if (pThis == NULL) - { - FCUnique(0x1); - FCThrow(kNullReferenceException); - } - - OBJECTREF pTarget = GetWeakReferenceTarget(pThis); - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - // If we found an object, or we're not a native COM weak reference, then we're done. Othewrise - // we can try to create a new RCW to the underlying native COM object if it's still alive. - if (pTarget != NULL || !IsNativeComWeakReferenceHandle(pThis->m_Handle)) - { - FC_GC_POLL_AND_RETURN_OBJREF(pTarget); - } - - FC_INNER_RETURN(Object*, LoadComWeakReferenceTarget(pThis, g_pObjectClass, GetEEFuncEntryPointMacro(WeakReferenceNative::GetTarget))); -#else // !FEATURE_COMINTEROP && !FEATURE_COMWRAPPERS - FC_GC_POLL_AND_RETURN_OBJREF(pTarget); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS -} -FCIMPLEND - -FCIMPL1(Object *, WeakReferenceOfTNative::GetTarget, WeakReferenceObject * pThisUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - if (pThis == NULL) - { - FCThrow(kNullReferenceException); - } - - OBJECTREF pTarget = GetWeakReferenceTarget(pThis); - - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - // If we found an object, or we're not a native COM weak reference, then we're done. Othewrise - // we can try to create a new RCW to the underlying native COM object if it's still alive. - if (pTarget != NULL || !IsNativeComWeakReferenceHandle(pThis->m_Handle)) - { - FC_GC_POLL_AND_RETURN_OBJREF(pTarget); - } - - FC_INNER_RETURN(Object*, LoadComWeakReferenceTarget(pThis, pThis->GetMethodTable()->GetInstantiation()[0], GetEEFuncEntryPointMacro(WeakReferenceOfTNative::GetTarget))); -#else // !FEATURE_COMINTEROP && !FEATURE_COMWRAPPERS - FC_GC_POLL_AND_RETURN_OBJREF(pTarget); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS -} -FCIMPLEND - -FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsAlive, WeakReferenceObject * pThisUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - if (pThis == NULL) - { - FCThrow(kNullReferenceException); - } - - BOOL fRet = GetWeakReferenceTarget(pThis) != NULL; - - FC_GC_POLL_RET(); - - FC_RETURN_BOOL(fRet); -} -FCIMPLEND - -#include - -//************************************************************************ - -#include - -// Slow path helper for setting the target of a weak reference. This code is used if a native COM weak reference might -// be required. -NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF target, LPVOID __me) -{ - FCALL_CONTRACT; - - FC_INNER_PROLOG_NO_ME_SETUP(); - HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, target, weakReference); - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - NewHolder comWeakHandleInfo(GetComWeakReferenceInfo(&target)); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - - WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(weakReference), &weakReference); - GCX_NOTRIGGER(); - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - // - // We have four combinations to handle here - // - // Existing target is a GC object, new target is a GC object: - // * Just store the new object in the handle - // - // Existing target is native COM weak reference, new target is native COM weak reference: - // * Release the existing IWeakReference* - // * Store the new IWeakReference* - // * Store the new object in the handle - // - // Existing target is native COM weak reference, new target is GC: - // * Release the existing IWeakReference* - // * Store null to the IWeakReference* field - // * Store the new object in the handle - // - // Existing target is GC, new target is native COM weak reference: - // * Destroy the existing handle - // * Allocate a new native COM weak handle for the new target - // - - if (IsNativeComWeakReferenceHandle(handle.RawHandle)) - { - // If the existing reference is a native COM weak reference, we need to delete its native COM info - // and update it with the new native COM info. If the incoming object is not an RCW that can use - // IWeakReference, then comWeakHandleInfo will be null. Therefore, no matter what the incoming - // object type is, we can unconditionally store comWeakHandleInfo to the object handle's extra data. - IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - NativeComWeakHandleInfo* existingInfo = reinterpret_cast(mgr->GetExtraInfoFromHandle(handle.Handle)); - mgr->SetExtraInfoForHandle(handle.Handle, HNDTYPE_WEAK_NATIVE_COM, reinterpret_cast(comWeakHandleInfo.GetValue())); - StoreObjectInHandle(handle.Handle, target); - - if (existingInfo != nullptr) - { - _ASSERTE(existingInfo->WeakReference != nullptr); - existingInfo->WeakReference->Release(); - delete existingInfo; - } - } - else if (comWeakHandleInfo != nullptr) - { - // The existing handle is not a native COM weak reference, but we need to store the new object in - // a native COM weak reference. Therefore we need to destroy the old handle and create a new native COM - // handle. The new handle needs to be allocated first to prevent the weak reference from holding - // a destroyed handle if we fail to allocate the new one. - _ASSERTE(!IsNativeComWeakReferenceHandle(handle.RawHandle)); - OBJECTHANDLE previousHandle = handle.RawHandle; - - handle.Handle = GetAppDomain()->CreateNativeComWeakHandle(target, comWeakHandleInfo); - handle.RawHandle = SetNativeComWeakReferenceHandle(handle.Handle); - - DestroyTypedHandle(previousHandle); - } - else -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - { - StoreObjectInHandle(handle.Handle, target); - } - -#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) - comWeakHandleInfo.SuppressRelease(); -#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS - - HELPER_METHOD_FRAME_END(); - FC_INNER_EPILOG(); -} - -FCIMPL2(void, WeakReferenceNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - OBJECTREF pTarget(pTargetUNSAFE); - - if (pThis == NULL) - { - FCUnique(0x1); - FCThrowVoid(kNullReferenceException); - } - - bool storedObject = false; - - OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); - { - if (handle == NULL || IS_SPECIAL_HANDLE(handle)) - { - ReleaseWeakHandleSpinLock(pThis, handle); - FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized")); - } - - // Switch to no-trigger after the handle was validate. FCThrow triggers. - GCX_NOTRIGGER(); - - // If the existing handle is a GC weak handle and the new target is not an RCW, then - // we can avoid setting up a helper method frame and just reset the handle directly. - if (!IsNativeComWeakReferenceHandle(handle)) - { - if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) - { - StoreObjectInHandle(handle, pTarget); - storedObject = true; - } - } - - // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows - // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly - // walked by the epilog decoder. - ReleaseWeakHandleSpinLock(pThis, handle); - } - - // If we reset the handle directly, then early out before setting up a helper method frame - if (storedObject) - { - FC_GC_POLL(); - return; - } - - FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceNative::SetTarget))); -} -FCIMPLEND - -FCIMPL2(void, WeakReferenceOfTNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - OBJECTREF pTarget(pTargetUNSAFE); - - if (pThis == NULL) - { - FCThrowVoid(kNullReferenceException); - } - - bool storedObject = false; - - OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); - { - if (handle == NULL || IS_SPECIAL_HANDLE(handle)) - { - ReleaseWeakHandleSpinLock(pThis, handle); - FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized")); - } - - // Switch to no-trigger after the handle was validate. FCThrow triggers. - GCX_NOTRIGGER(); - - // If the existing handle is a GC weak handle and the new target is not an RCW, then - // we can avoid setting up a helper method frame and just reset the handle directly. - if (!IsNativeComWeakReferenceHandle(handle)) +#endif { - if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) +#ifdef FEATURE_COMWRAPPERS + bool isAggregated = false; + pWeakReferenceSource = reinterpret_cast(ComWrappersNative::GetIdentityForObject(&objRef, IID_IWeakReferenceSource, pWrapperId, &isAggregated)); + if (isAggregated) { - StoreObjectInHandle(handle, pTarget); - storedObject = true; + // If the RCW is an aggregated RCW, then the managed object cannot be recreated from the IUnknown as the outer IUnknown wraps the managed object. + // In this case, don't create a weak reference backed by a COM weak reference. + pWeakReferenceSource = nullptr; } +#endif } - // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows - // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly - // walked by the epilog decoder. - ReleaseWeakHandleSpinLock(pThis, handle); - } - - // If we reset the handle directly, then early out before setting up a helper method frame - if (storedObject) - { - FC_GC_POLL(); - return; - } - - FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceOfTNative::SetTarget))); -} -FCIMPLEND - -#include - -//************************************************************************ - -FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) -{ - FCALL_CONTRACT; - - WEAKREFERENCEREF pThis(pThisUNSAFE); - - if (pThis == NULL) - { - FCUnique(0x1); - FCThrow(kNullReferenceException); + GCPROTECT_END(); } - BOOL trackResurrection = FALSE; - OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + if (pWeakReferenceSource != nullptr) { - GCX_NOTRIGGER(); - - if (handle == NULL) + SafeComHolderPreemp weakReferenceHolder; + if (!FAILED(pWeakReferenceSource->GetWeakReference(&weakReferenceHolder))) { - trackResurrection = FALSE; + weakReferenceHolder.SuppressRelease(); + pWeakReference = weakReferenceHolder.GetValue(); } - else - if (IS_SPECIAL_HANDLE(handle)) - { - trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); - } - else - { - trackResurrection = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; - } - - ReleaseWeakHandleSpinLock(pThis, handle); } - FC_GC_POLL_RET(); - FC_RETURN_BOOL(trackResurrection); + END_QCALL; + return pWeakReference; } -FCIMPLEND -FCIMPL1(FC_BOOL_RET, WeakReferenceOfTNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) +FCIMPL1(FC_BOOL_RET, ComAwareWeakReferenceNative::HasInteropInfo, Object* pObject) { FCALL_CONTRACT; + _ASSERTE(pObject != nullptr); - WEAKREFERENCEREF pThis(pThisUNSAFE); - - if (pThis == NULL) - { - FCThrow(kNullReferenceException); - } - - BOOL trackResurrection = FALSE; - OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); - { - GCX_NOTRIGGER(); - - if (handle == NULL) - { - trackResurrection = FALSE; - } - else - if (IS_SPECIAL_HANDLE(handle)) - { - trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); - } - else - { - trackResurrection = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; - } - - ReleaseWeakHandleSpinLock(pThis, handle); - } - - FC_GC_POLL_RET(); - FC_RETURN_BOOL(trackResurrection); + SyncBlock* pSyncBlock = pObject->PassiveGetSyncBlock(); + _ASSERTE(pSyncBlock != nullptr); + return pSyncBlock->GetInteropInfoNoCreate() != nullptr; } FCIMPLEND -//************************************************************************ +#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS diff --git a/src/coreclr/vm/weakreferencenative.h b/src/coreclr/vm/weakreferencenative.h index 387d6df2e2524..0586d730f6a03 100644 --- a/src/coreclr/vm/weakreferencenative.h +++ b/src/coreclr/vm/weakreferencenative.h @@ -11,31 +11,17 @@ #ifndef _WEAKREFERENCENATIVE_H #define _WEAKREFERENCENATIVE_H -// -// The implementations of WeakReferenceNative and WeakReferenceOfTNative are identical, but the managed signatures -// are different. WeakReferenceOfTNative has strongly typed signatures. It is necessary for correct security transparancy -// annotations without compromising inlining (security critical code cannot be inlined into security neutral code). -// +#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) -class WeakReferenceNative +class ComAwareWeakReferenceNative { public: - static FCDECL3(void, Create, WeakReferenceObject * pThis, Object * pTarget, CLR_BOOL trackResurrection); - static FCDECL1(void, Finalize, WeakReferenceObject * pThis); - static FCDECL1(Object *, GetTarget, WeakReferenceObject * pThis); - static FCDECL2(void, SetTarget, WeakReferenceObject * pThis, Object * pTarget); - static FCDECL1(FC_BOOL_RET, IsTrackResurrection, WeakReferenceObject * pThis); - static FCDECL1(FC_BOOL_RET, IsAlive, WeakReferenceObject * pThis); + static FCDECL1(FC_BOOL_RET, HasInteropInfo, Object* pObject); }; -class WeakReferenceOfTNative -{ -public: - static FCDECL3(void, Create, WeakReferenceObject * pThis, Object * pTarget, CLR_BOOL trackResurrection); - static FCDECL1(void, Finalize, WeakReferenceObject * pThis); - static FCDECL1(Object *, GetTarget, WeakReferenceObject * pThis); - static FCDECL2(void, SetTarget, WeakReferenceObject * pThis, Object * pTarget); - static FCDECL1(FC_BOOL_RET, IsTrackResurrection, WeakReferenceObject * pThis); -}; +extern "C" void QCALLTYPE ComWeakRefToObject(IWeakReference * pComWeakReference, INT64 wrapperId, QCall::ObjectHandleOnStack retRcw); +extern "C" IWeakReference * QCALLTYPE ObjectToComWeakRef(QCall::ObjectHandleOnStack obj, INT64* wrapperId); + +#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS #endif // _WEAKREFERENCENATIVE_H 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 24208f62b29c4..eadfb632bf576 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 @@ -1192,6 +1192,7 @@ + Common\Interop\Interop.Calendar.cs @@ -2485,4 +2486,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/ComAwareWeakReference.cs b/src/libraries/System.Private.CoreLib/src/System/ComAwareWeakReference.cs new file mode 100644 index 0000000000000..c6e7698c5768c --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/ComAwareWeakReference.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + +using static System.WeakReferenceHandleTags; + +namespace System +{ + internal sealed partial class ComAwareWeakReference + { + // _weakHandle is effectively readonly. + // the only place where we change it after construction is in finalizer. + private nint _weakHandle; + private ComInfo? _comInfo; + + internal sealed class ComInfo + { + // _pComWeakRef is effectively readonly. + // the only place where we change it after construction is in finalizer. + private IntPtr _pComWeakRef; + private readonly long _wrapperId; + + internal object? ResolveTarget() + { + return ComWeakRefToObject(_pComWeakRef, _wrapperId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ComInfo? FromObject(object? target) + { + if (target == null || !PossiblyComObject(target)) + return null; + + return FromObjectSlow(target); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe ComInfo? FromObjectSlow(object target) + { + IntPtr pComWeakRef = ObjectToComWeakRef(target, out long wrapperId); + if (pComWeakRef == 0) + return null; + + try + { + return new ComInfo(pComWeakRef, wrapperId); + } + catch (OutOfMemoryException) + { + // we did not create an object, so ComInfo finalizer will not run + Marshal.Release(pComWeakRef); + throw; + } + } + + private ComInfo(IntPtr pComWeakRef, long wrapperId) + { + Debug.Assert(pComWeakRef != IntPtr.Zero); + _pComWeakRef = pComWeakRef; + _wrapperId = wrapperId; + } + + ~ComInfo() + { + Marshal.Release(_pComWeakRef); + // our use pattern guarantees that the instance is not reachable after this. + // clear the pointer just in case that gets somehow broken. + _pComWeakRef = 0; + } + } + + private ComAwareWeakReference(nint weakHandle) + { + Debug.Assert(weakHandle != 0); + _weakHandle = weakHandle; + } + + ~ComAwareWeakReference() + { + GCHandle.InternalFree(_weakHandle); + // our use pattern guarantees that the instance is not reachable after this. + // clear the pointer just in case that gets somehow broken. + _weakHandle = 0; + } + + private void SetTarget(object? target, ComInfo? comInfo) + { + // NOTE: ComAwareWeakReference is an internal implementation detail and + // instances are never exposed publicly, thus we can use "this" for locking + lock (this) + { + GCHandle.InternalSet(_weakHandle, target); + _comInfo = comInfo; + } + } + + internal object? Target => GCHandle.InternalGet(_weakHandle) ?? RehydrateTarget(); + + private object? RehydrateTarget() + { + object? target = null; + lock (this) + { + if (_comInfo != null) + { + // check if the target is still null + target = GCHandle.InternalGet(_weakHandle); + if (target == null) + { + // resolve and reset + target = _comInfo.ResolveTarget(); + if (target != null) + GCHandle.InternalSet(_weakHandle, target); + } + } + } + + return target; + } + + private static ComAwareWeakReference EnsureComAwareReference(ref nint taggedHandle) + { + nint current = taggedHandle; + if ((current & ComAwareBit) == 0) + { + ComAwareWeakReference newRef = new ComAwareWeakReference(taggedHandle & ~HandleTagBits); + nint newHandle = GCHandle.InternalAlloc(newRef, GCHandleType.Normal); + nint newTaggedHandle = newHandle | ComAwareBit | (taggedHandle & TracksResurrectionBit); + if (Interlocked.CompareExchange(ref taggedHandle, newTaggedHandle, current) == current) + { + // success. + return newRef; + } + + // someone beat us to it. (this is rare) + GCHandle.InternalFree(newHandle); + GC.SuppressFinalize(newRef); + } + + return Unsafe.As(GCHandle.InternalGet(taggedHandle & ~HandleTagBits)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static object? GetTarget(nint taggedHandle) + { + Debug.Assert((taggedHandle & ComAwareBit) != 0); + return Unsafe.As(GCHandle.InternalGet(taggedHandle & ~HandleTagBits)).Target; + } + + internal static nint GetWeakHandle(nint taggedHandle) + { + Debug.Assert((taggedHandle & ComAwareBit) != 0); + return Unsafe.As(GCHandle.InternalGet(taggedHandle & ~HandleTagBits))._weakHandle; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void SetTarget(ref nint taggedHandle, object? target, ComInfo? comInfo) + { + ComAwareWeakReference comAwareRef = comInfo != null ? + EnsureComAwareReference(ref taggedHandle) : + Unsafe.As(GCHandle.InternalGet(taggedHandle & ~HandleTagBits)); + + comAwareRef.SetTarget(target, comInfo); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void SetComInfoInConstructor(ref nint taggedHandle, ComInfo comInfo) + { + Debug.Assert((taggedHandle & ComAwareBit) == 0); + ComAwareWeakReference comAwareRef = new ComAwareWeakReference(taggedHandle & ~HandleTagBits); + nint newHandle = GCHandle.InternalAlloc(comAwareRef, GCHandleType.Normal); + taggedHandle = newHandle | ComAwareBit | (taggedHandle & TracksResurrectionBit); + comAwareRef._comInfo = comInfo; + } + } +} +#endif diff --git a/src/libraries/System.Private.CoreLib/src/System/WeakReference.T.cs b/src/libraries/System.Private.CoreLib/src/System/WeakReference.T.cs index 3d48ec61d811b..36cb48135dd89 100644 --- a/src/libraries/System.Private.CoreLib/src/System/WeakReference.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/WeakReference.T.cs @@ -7,6 +7,8 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics; +using static System.WeakReferenceHandleTags; + namespace System { [Serializable] @@ -15,23 +17,15 @@ namespace System public sealed partial class WeakReference : ISerializable where T : class? { - // If you fix bugs here, please fix them in WeakReference at the same time. + // If you fix bugs here, please fix them in WeakReference at the same time. // Most methods using the handle should use GC.KeepAlive(this) to avoid potential handle recycling // attacks (i.e. if the WeakReference instance is finalized away underneath you when you're still // handling a cached value of the handle then the handle could be freed and reused). - -#if !CORECLR - // the handle field is effectively readonly until the object is finalized. - private IntPtr _handleAndKind; - - // the lowermost bit is used to indicate whether the handle is tracking resurrection - private const nint TracksResurrectionBit = 1; -#endif + private nint _taggedHandle; // Creates a new WeakReference that keeps track of target. // Assumes a Short Weak Reference (ie TrackResurrection is false.) - // public WeakReference(T target) : this(target, false) { @@ -64,7 +58,6 @@ private WeakReference(SerializationInfo info, StreamingContext context) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetTarget([MaybeNullWhen(false), NotNullWhen(true)] out T target) { - // Call the worker method that has more performant but less user friendly signature. T? o = this.Target; target = o!; return o != null; @@ -78,32 +71,47 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("TrackResurrection", IsTrackResurrection()); // Do not rename (binary serialization) } -#if !CORECLR // Returns a boolean indicating whether or not we're tracking objects until they're collected (true) // or just until they're finalized (false). - private bool IsTrackResurrection() => (_handleAndKind & TracksResurrectionBit) != 0; + private bool IsTrackResurrection() => (_taggedHandle & TracksResurrectionBit) != 0; // Creates a new WeakReference that keeps track of target. private void Create(T target, bool trackResurrection) { - IntPtr h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); - _handleAndKind = trackResurrection ? + nint h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + _taggedHandle = trackResurrection ? h | TracksResurrectionBit : h; - } - private IntPtr Handle => _handleAndKind & ~TracksResurrectionBit; +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + ComAwareWeakReference.ComInfo? comInfo = ComAwareWeakReference.ComInfo.FromObject(target); + if (comInfo != null) + { + ComAwareWeakReference.SetComInfoInConstructor(ref _taggedHandle, comInfo); + } +#endif + } public void SetTarget(T target) { - IntPtr h = Handle; + nint th = _taggedHandle & ~TracksResurrectionBit; + // Should only happen for corner cases, like using a WeakReference from a finalizer. // GC can finalize the instance if it becomes F-Reachable. // That, however, cannot happen while we use the instance. - if (default(IntPtr) == h) + if (th == 0) throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); - GCHandle.InternalSet(h, target); +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + var comInfo = ComAwareWeakReference.ComInfo.FromObject(target); + if ((th & ComAwareBit) != 0 || comInfo != null) + { + ComAwareWeakReference.SetTarget(ref _taggedHandle, target, comInfo); + return; + } +#endif + + GCHandle.InternalSet(th, target); // must keep the instance alive as long as we use the handle. GC.KeepAlive(this); @@ -111,17 +119,24 @@ public void SetTarget(T target) private T? Target { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - IntPtr h = Handle; + nint th = _taggedHandle & ~TracksResurrectionBit; + // Should only happen for corner cases, like using a WeakReference from a finalizer. // GC can finalize the instance if it becomes F-Reachable. // That, however, cannot happen while we use the instance. - if (default(IntPtr) == h) + if (th == 0) return default; +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + if ((th & ComAwareBit) != 0) + return Unsafe.As(ComAwareWeakReference.GetTarget(th)); +#endif + // unsafe cast is ok as the handle cannot be destroyed and recycled while we keep the instance alive - T? target = Unsafe.As(GCHandle.InternalGet(h)); + T? target = Unsafe.As(GCHandle.InternalGet(th)); // must keep the instance alive as long as we use the handle. GC.KeepAlive(this); @@ -140,6 +155,5 @@ private T? Target } #pragma warning restore CA1821 // Remove empty Finalizers -#endif } } diff --git a/src/libraries/System.Private.CoreLib/src/System/WeakReference.cs b/src/libraries/System.Private.CoreLib/src/System/WeakReference.cs index 2f56806b48c83..81c33fd07445e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/WeakReference.cs +++ b/src/libraries/System.Private.CoreLib/src/System/WeakReference.cs @@ -2,11 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using static System.WeakReferenceHandleTags; + namespace System { + internal static class WeakReferenceHandleTags + { + // the lowermost bit is used to indicate whether the handle is tracking resurrection + // handles are at least 2-byte aligned, so we can use one bit for tagging + internal const nint TracksResurrectionBit = 1; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + // one more bit is used to track whether the handle refers to an instance of ComAwareWeakReference + // we can use this bit because on COM-supporting platforms a handle is at least 4-byte aligned + internal const nint ComAwareBit = 2; + internal const nint HandleTagBits = TracksResurrectionBit | ComAwareBit; +#else + internal const nint HandleTagBits = TracksResurrectionBit; +#endif + } + [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class WeakReference : ISerializable @@ -17,13 +36,7 @@ public partial class WeakReference : ISerializable // attacks (i.e. if the WeakReference instance is finalized away underneath you when you're still // handling a cached value of the handle then the handle could be freed and reused). -#if !CORECLR - // the handle field is effectively readonly until the object is finalized. - private IntPtr _handleAndKind; - - // the lowermost bit is used to indicate whether the handle is tracking resurrection - private const nint TracksResurrectionBit = 1; -#endif + private nint _taggedHandle; // Creates a new WeakReference that keeps track of target. // Assumes a Short Weak Reference (ie TrackResurrection is false.) @@ -60,20 +73,40 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte // or just until they're finalized (false). public virtual bool TrackResurrection => IsTrackResurrection(); -#if !CORECLR private void Create(object? target, bool trackResurrection) { - IntPtr h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); - _handleAndKind = trackResurrection ? + nint h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + _taggedHandle = trackResurrection ? h | TracksResurrectionBit : h; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + ComAwareWeakReference.ComInfo? comInfo = ComAwareWeakReference.ComInfo.FromObject(target); + if (comInfo != null) + { + ComAwareWeakReference.SetComInfoInConstructor(ref _taggedHandle, comInfo); + } +#endif } // Returns a boolean indicating whether or not we're tracking objects until they're collected (true) // or just until they're finalized (false). - private bool IsTrackResurrection() => (_handleAndKind & TracksResurrectionBit) != 0; + private bool IsTrackResurrection() => (_taggedHandle & TracksResurrectionBit) != 0; - internal IntPtr Handle => _handleAndKind & ~TracksResurrectionBit; + internal nint WeakHandle + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + nint th = _taggedHandle; + +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + if ((th & ComAwareBit) != 0) + return ComAwareWeakReference.GetWeakHandle(th); +#endif + return th & ~HandleTagBits; + } + } // Determines whether or not this instance of WeakReference still refers to an object // that has not been collected. @@ -81,14 +114,14 @@ public virtual bool IsAlive { get { - IntPtr h = Handle; + nint wh = WeakHandle; // In determining whether it is valid to use this object, we need to at least expose this // without throwing an exception. - if (default(IntPtr) == h) + if (wh == 0) return false; - bool result = GCHandle.InternalGet(h) != null; + bool result = GCHandle.InternalGet(wh) != null; // must keep the instance alive as long as we use the handle. GC.KeepAlive(this); @@ -101,9 +134,11 @@ public virtual bool IsAlive // Or sets it. public virtual object? Target { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - IntPtr h = Handle; + nint th = _taggedHandle & ~TracksResurrectionBit; + // Should only happen for corner cases, like using a WeakReference from a finalizer. // GC can finalize the instance if it becomes F-Reachable. // That, however, cannot happen while we use the instance. @@ -114,10 +149,16 @@ public virtual object? Target // There is a possibility that a derived type overrides the default finalizer and arranges concurrent access. // There is nothing that we can do about that and a few other exotic ways to break this. // - if (default(IntPtr) == h) + if (th == 0) return default; - object? target = GCHandle.InternalGet(h); +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + if ((th & ComAwareBit) != 0) + return ComAwareWeakReference.GetTarget(th); +#endif + + // unsafe cast is ok as the handle cannot be destroyed and recycled while we keep the instance alive + object? target = GCHandle.InternalGet(th); // must keep the instance alive as long as we use the handle. GC.KeepAlive(this); @@ -127,13 +168,24 @@ public virtual object? Target set { - IntPtr h = Handle; + nint th = _taggedHandle & ~TracksResurrectionBit; + // Should only happen for corner cases, like using a WeakReference from a finalizer. - // See the comment in the getter. - if (default(IntPtr) == h) + // GC can finalize the instance if it becomes F-Reachable. + // That, however, cannot happen while we use the instance. + if (th == 0) throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); - GCHandle.InternalSet(h, value); +#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS + var comInfo = ComAwareWeakReference.ComInfo.FromObject(value); + if ((th & ComAwareBit) != 0 || comInfo != null) + { + ComAwareWeakReference.SetTarget(ref _taggedHandle, value, comInfo); + return; + } +#endif + + GCHandle.InternalSet(th, value); // must keep the instance alive as long as we use the handle. GC.KeepAlive(this); @@ -152,15 +204,14 @@ public virtual object? Target Debug.Assert(this.GetType() != typeof(WeakReference)); - IntPtr handle = Handle; - if (handle != default(IntPtr)) + nint handle = _taggedHandle & ~HandleTagBits; + if (handle != 0) { GCHandle.InternalFree(handle); - // keep the bit that indicates whether this reference was tracking resurrection - _handleAndKind &= TracksResurrectionBit; + // keep the bit that indicates whether this reference was tracking resurrection, clear the rest. + _taggedHandle &= TracksResurrectionBit; } } -#endif } } diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 4c161f4669457..7e32a0a905cca 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -654,7 +654,7 @@ typedef struct { typedef struct { MonoObject object; - gsize handleAndKind; + gsize taggedHandle; } MonoWeakReference; /* Safely access System.Delegate from native code */ diff --git a/src/mono/mono/metadata/sgen-mono.c b/src/mono/mono/metadata/sgen-mono.c index 0d0cb4b33d457..603f4e93a2ae1 100644 --- a/src/mono/mono/metadata/sgen-mono.c +++ b/src/mono/mono/metadata/sgen-mono.c @@ -465,9 +465,10 @@ sgen_client_object_finalize_eagerly (GCObject *obj) { if (obj->vtable->gc_bits & SGEN_GC_BIT_WEAKREF) { MonoWeakReference *wr = (MonoWeakReference*)obj; - MonoGCHandle gc_handle = (MonoGCHandle)(wr->handleAndKind & ~(gsize)1); + MonoGCHandle gc_handle = (MonoGCHandle)(wr->taggedHandle & ~(gsize)1); mono_gchandle_free_internal (gc_handle); - wr->handleAndKind &= (gsize)1; + // keep the bit that indicates whether this reference was tracking resurrection, clear the rest. + wr->taggedHandle &= (gsize)1; return TRUE; }