From 6dae493bb61face5c55a256c49404b35c271ce78 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 21 Jan 2020 15:39:18 -0800 Subject: [PATCH 01/83] Add API to SPCL. --- .../System.Private.CoreLib.Shared.projitems | 1 + .../CompilerServices/RuntimeHelpers.cs | 12 ++ .../Runtime/InteropServices/ComWrappers.cs | 172 ++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs 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 62e69dd3b3663d..ce869188c71dde 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 @@ -676,6 +676,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 3f06b3def7f29c..88b44b070d58a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -106,5 +106,17 @@ public static void PrepareConstrainedRegionsNoOP() internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + + /// + /// Allocate memory that is associated with the and + /// will be freed if and when the is unloaded. + /// + /// Type associated with the allocated memory. + /// Amount of memory in bytes to allocate. + /// The allocated memory + public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) + { + throw new NotImplementedException(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs new file mode 100644 index 00000000000000..b6ea53ca6e5b69 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Enumeration of flags for . + /// + [Flags] + public enum CreateComInterfaceFlags + { + None = 0, + + /// + /// The caller will provide an IUnknown Vtable. + /// + CallerDefinedIUnknown = 1, + + /// + /// Ignore the internal cache when creating a instance. + /// + IgnoreCache = 2, + + /// + /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. + /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown + /// and as such none should be supplied by the caller. + /// + TrackerSupport = 4, + } + + /// + /// Enumeration of flags for . + /// + [Flags] + public enum CreateObjectFlags + { + None = 0, + + /// + /// Indicate if the supplied external COM object implements the IReferenceTracker. + /// + TrackerObject = 1, + + /// + /// Ignore the internal cache when creating an object. + /// + IgnoreCache = 2, + } + + /// + /// Class for managing wrappers of COM IUnknown types. + /// + [CLSCompliant(false)] + public abstract class ComWrappers + { + /// + /// Interface type and pointer to targeted VTable. + /// + public struct ComInterfaceEntry + { + /// + /// Interface IID. + /// + public Guid IID; + + /// + /// Must be pinned memory that is owned by the implementer of . + /// The memory must live as long as any COM interface consuming the table exists. + /// + public IntPtr Vtable; + } + + /// + /// ABI for function dispatch of a COM interface. + /// + public struct ComInterfaceDispatch + { + public IntPtr vftbl; + + /// + /// Given a from a generated VTable, convert to the target type. + /// + /// Desired type. + /// Pointer supplied to VTable function entry. + /// Instance of type associated with dispatched function call. + public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class + { + // See the CCW dispatch section in the runtime for details on the masking below. + const long DispatchThisPtrMask = ~0xfL; + var comInstance = *(ComInterfaceInstance**)(((long)dispatchPtr) & DispatchThisPtrMask); + + return Unsafe.As(GCHandle.InternalGet(comInstance->GcHandle)); + } + + private struct ComInterfaceInstance + { + public IntPtr GcHandle; + } + } + + /// + /// Create an COM representation of the supplied object that can be passed to an non-managed environment. + /// + /// A GC Handle to the managed object to expose outside the .NET runtime. + /// Flags used to configure the generated interface. + /// The generated COM interface that can be passed outside the .NET runtime. + public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) + { + throw new NotImplementedException(); + } + + /// + /// Compute the desired VTables for respecting the values of . + /// + /// Target of the returned VTables. + /// Flags used to compute VTables. + /// The number of elements contained in the returned memory. + /// pointer containing memory for all COM interface entries. + /// + /// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been + /// allocated with the API. + /// + protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); + + /// + /// Get the currently registered managed object or creates a new managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// Returns a managed object associated with the supplied external COM object. + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + { + throw new NotImplementedException(); + } + + /// + /// Create a managed object for respecting the values of . + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// Returns a managed object associated with the supplied external COM object. + protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); + + /// + /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. + /// + /// + /// This function can only be called a single time. Subsequent calls to this function will result + /// in a being thrown. + /// + public void RegisterForReferenceTrackerHost() + { + throw new NotImplementedException(); + } + + /// + /// Get the runtime provided IUnknown implementation. + /// + /// Function pointer to QueryInterface. + /// Function pointer to AddRef. + /// Function pointer to Release. + /// [TODO] Consider just returning a struct. Perhaps an Enum for input? + protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file From aa43ec825b99b4010006149ce2dc27f318ab1ad2 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 21 Jan 2020 15:58:42 -0800 Subject: [PATCH 02/83] Revert previous work. --- .../System.Private.CoreLib.Shared.projitems | 1 - .../CompilerServices/RuntimeHelpers.cs | 12 -- .../Runtime/InteropServices/ComWrappers.cs | 172 ------------------ 3 files changed, 185 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs 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 ce869188c71dde..62e69dd3b3663d 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 @@ -676,7 +676,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 88b44b070d58a2..3f06b3def7f29c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -106,17 +106,5 @@ public static void PrepareConstrainedRegionsNoOP() internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; - - /// - /// Allocate memory that is associated with the and - /// will be freed if and when the is unloaded. - /// - /// Type associated with the allocated memory. - /// Amount of memory in bytes to allocate. - /// The allocated memory - public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) - { - throw new NotImplementedException(); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs deleted file mode 100644 index b6ea53ca6e5b69..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Internal.Runtime.CompilerServices; - -namespace System.Runtime.InteropServices -{ - /// - /// Enumeration of flags for . - /// - [Flags] - public enum CreateComInterfaceFlags - { - None = 0, - - /// - /// The caller will provide an IUnknown Vtable. - /// - CallerDefinedIUnknown = 1, - - /// - /// Ignore the internal cache when creating a instance. - /// - IgnoreCache = 2, - - /// - /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. - /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown - /// and as such none should be supplied by the caller. - /// - TrackerSupport = 4, - } - - /// - /// Enumeration of flags for . - /// - [Flags] - public enum CreateObjectFlags - { - None = 0, - - /// - /// Indicate if the supplied external COM object implements the IReferenceTracker. - /// - TrackerObject = 1, - - /// - /// Ignore the internal cache when creating an object. - /// - IgnoreCache = 2, - } - - /// - /// Class for managing wrappers of COM IUnknown types. - /// - [CLSCompliant(false)] - public abstract class ComWrappers - { - /// - /// Interface type and pointer to targeted VTable. - /// - public struct ComInterfaceEntry - { - /// - /// Interface IID. - /// - public Guid IID; - - /// - /// Must be pinned memory that is owned by the implementer of . - /// The memory must live as long as any COM interface consuming the table exists. - /// - public IntPtr Vtable; - } - - /// - /// ABI for function dispatch of a COM interface. - /// - public struct ComInterfaceDispatch - { - public IntPtr vftbl; - - /// - /// Given a from a generated VTable, convert to the target type. - /// - /// Desired type. - /// Pointer supplied to VTable function entry. - /// Instance of type associated with dispatched function call. - public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class - { - // See the CCW dispatch section in the runtime for details on the masking below. - const long DispatchThisPtrMask = ~0xfL; - var comInstance = *(ComInterfaceInstance**)(((long)dispatchPtr) & DispatchThisPtrMask); - - return Unsafe.As(GCHandle.InternalGet(comInstance->GcHandle)); - } - - private struct ComInterfaceInstance - { - public IntPtr GcHandle; - } - } - - /// - /// Create an COM representation of the supplied object that can be passed to an non-managed environment. - /// - /// A GC Handle to the managed object to expose outside the .NET runtime. - /// Flags used to configure the generated interface. - /// The generated COM interface that can be passed outside the .NET runtime. - public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) - { - throw new NotImplementedException(); - } - - /// - /// Compute the desired VTables for respecting the values of . - /// - /// Target of the returned VTables. - /// Flags used to compute VTables. - /// The number of elements contained in the returned memory. - /// pointer containing memory for all COM interface entries. - /// - /// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been - /// allocated with the API. - /// - protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - - /// - /// Get the currently registered managed object or creates a new managed object and registers it. - /// - /// Object to import for usage into the .NET runtime. - /// Flags used to describe the external object. - /// Returns a managed object associated with the supplied external COM object. - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) - { - throw new NotImplementedException(); - } - - /// - /// Create a managed object for respecting the values of . - /// - /// Object to import for usage into the .NET runtime. - /// Flags used to describe the external object. - /// Returns a managed object associated with the supplied external COM object. - protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); - - /// - /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. - /// - /// - /// This function can only be called a single time. Subsequent calls to this function will result - /// in a being thrown. - /// - public void RegisterForReferenceTrackerHost() - { - throw new NotImplementedException(); - } - - /// - /// Get the runtime provided IUnknown implementation. - /// - /// Function pointer to QueryInterface. - /// Function pointer to AddRef. - /// Function pointer to Release. - /// [TODO] Consider just returning a struct. Perhaps an Enum for input? - protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file From e8846f147a47145815dbc043928761bc0b4571d0 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 21 Jan 2020 16:11:46 -0800 Subject: [PATCH 03/83] Add API in stub form. --- .../System.Private.CoreLib.csproj | 4 + .../RuntimeHelpers.CoreCLR.cs | 12 ++ .../Runtime/InteropServices/ComWrappers.cs | 172 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 881d4601cd6ec3..2d705c4960e09d 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -323,6 +323,10 @@ + + + + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 117e7e26dc6ea2..9e7975740740b8 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -264,6 +264,18 @@ internal static unsafe bool ObjectHasComponentSize(object obj) return (MethodTable *)Unsafe.Add(ref Unsafe.As(ref obj.GetRawData()), -1); } + + /// + /// Allocate memory that is associated with the and + /// will be freed if and when the is unloaded. + /// + /// Type associated with the allocated memory. + /// Amount of memory in bytes to allocate. + /// The allocated memory + public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) + { + throw new NotImplementedException(); + } } // Helper class to assist with unsafe pinning of arbitrary objects. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs new file mode 100644 index 00000000000000..b6ea53ca6e5b69 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Enumeration of flags for . + /// + [Flags] + public enum CreateComInterfaceFlags + { + None = 0, + + /// + /// The caller will provide an IUnknown Vtable. + /// + CallerDefinedIUnknown = 1, + + /// + /// Ignore the internal cache when creating a instance. + /// + IgnoreCache = 2, + + /// + /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. + /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown + /// and as such none should be supplied by the caller. + /// + TrackerSupport = 4, + } + + /// + /// Enumeration of flags for . + /// + [Flags] + public enum CreateObjectFlags + { + None = 0, + + /// + /// Indicate if the supplied external COM object implements the IReferenceTracker. + /// + TrackerObject = 1, + + /// + /// Ignore the internal cache when creating an object. + /// + IgnoreCache = 2, + } + + /// + /// Class for managing wrappers of COM IUnknown types. + /// + [CLSCompliant(false)] + public abstract class ComWrappers + { + /// + /// Interface type and pointer to targeted VTable. + /// + public struct ComInterfaceEntry + { + /// + /// Interface IID. + /// + public Guid IID; + + /// + /// Must be pinned memory that is owned by the implementer of . + /// The memory must live as long as any COM interface consuming the table exists. + /// + public IntPtr Vtable; + } + + /// + /// ABI for function dispatch of a COM interface. + /// + public struct ComInterfaceDispatch + { + public IntPtr vftbl; + + /// + /// Given a from a generated VTable, convert to the target type. + /// + /// Desired type. + /// Pointer supplied to VTable function entry. + /// Instance of type associated with dispatched function call. + public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class + { + // See the CCW dispatch section in the runtime for details on the masking below. + const long DispatchThisPtrMask = ~0xfL; + var comInstance = *(ComInterfaceInstance**)(((long)dispatchPtr) & DispatchThisPtrMask); + + return Unsafe.As(GCHandle.InternalGet(comInstance->GcHandle)); + } + + private struct ComInterfaceInstance + { + public IntPtr GcHandle; + } + } + + /// + /// Create an COM representation of the supplied object that can be passed to an non-managed environment. + /// + /// A GC Handle to the managed object to expose outside the .NET runtime. + /// Flags used to configure the generated interface. + /// The generated COM interface that can be passed outside the .NET runtime. + public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) + { + throw new NotImplementedException(); + } + + /// + /// Compute the desired VTables for respecting the values of . + /// + /// Target of the returned VTables. + /// Flags used to compute VTables. + /// The number of elements contained in the returned memory. + /// pointer containing memory for all COM interface entries. + /// + /// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been + /// allocated with the API. + /// + protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); + + /// + /// Get the currently registered managed object or creates a new managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// Returns a managed object associated with the supplied external COM object. + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + { + throw new NotImplementedException(); + } + + /// + /// Create a managed object for respecting the values of . + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// Returns a managed object associated with the supplied external COM object. + protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); + + /// + /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. + /// + /// + /// This function can only be called a single time. Subsequent calls to this function will result + /// in a being thrown. + /// + public void RegisterForReferenceTrackerHost() + { + throw new NotImplementedException(); + } + + /// + /// Get the runtime provided IUnknown implementation. + /// + /// Function pointer to QueryInterface. + /// Function pointer to AddRef. + /// Function pointer to Release. + /// [TODO] Consider just returning a struct. Perhaps an Enum for input? + protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file From 7e0c32883d219ebf6896f6047a8e323953880c44 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 22 Jan 2020 12:24:51 -0800 Subject: [PATCH 04/83] Implement RuntimeHelpers.AllocateTypeAssociatedMemory() --- .../RuntimeHelpers.CoreCLR.cs | 12 +++++++++- src/coreclr/src/vm/ecalllist.h | 1 + src/coreclr/src/vm/runtimehandles.cpp | 22 +++++++++++++++++++ src/coreclr/src/vm/runtimehandles.h | 3 +++ .../System.Runtime/ref/System.Runtime.cs | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 9e7975740740b8..705e611d7e1645 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -274,8 +274,18 @@ internal static unsafe bool ObjectHasComponentSize(object obj) /// The allocated memory public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) { - throw new NotImplementedException(); + RuntimeType? rt = type as RuntimeType; + if (rt == null) + throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); + + if (size < 0) + throw new ArgumentOutOfRangeException(nameof(size)); + + return AllocateTypeAssociatedMemoryInternal(new QCallTypeHandle(ref rt), (uint)size); } + + [DllImport(RuntimeHelpers.QCall)] + private static extern IntPtr AllocateTypeAssociatedMemoryInternal(QCallTypeHandle type, uint size); } // Helper class to assist with unsafe pinning of arbitrary objects. diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 5a20f274587670..39ea0d1d199f73 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -921,6 +921,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject) + QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncEnd() FCFuncStart(gContextSynchronizationFuncs) diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index e740579e771d38..2b4cad229bb447 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -1721,6 +1721,28 @@ FCIMPL1(IMDInternalImport*, RuntimeTypeHandle::GetMetadataImport, ReflectClassBa } FCIMPLEND +PVOID QCALLTYPE RuntimeTypeHandle::AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size) +{ + QCALL_CONTRACT; + + void *allocatedMemory = nullptr; + + BEGIN_QCALL; + + TypeHandle typeHandle = type.AsTypeHandle(); + _ASSERTE(!typeHandle.IsNull()); + + // Get the loader allocator for the associated type. + // Allocating using the type's associated loader allocator means + // that the memory will be freed when the type is unloaded. + PTR_LoaderAllocator loaderAllocator = typeHandle.GetMethodTable()->GetLoaderAllocator(); + LoaderHeap* loaderHeap = loaderAllocator->GetHighFrequencyHeap(); + allocatedMemory = loaderHeap->AllocMem(S_SIZE_T(size)); + + END_QCALL; + + return allocatedMemory; +} //*********************************************************************************** //*********************************************************************************** diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 36bc4ada54553b..7787e016753058 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -261,6 +261,9 @@ class RuntimeTypeHandle { static FCDECL1(IMDInternalImport*, GetMetadataImport, ReflectClassBaseObject * pModuleUNSAFE); + + static + PVOID QCALLTYPE AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size); }; class RuntimeMethodHandle { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 467d544051fa53..0f0362a3d9e5e9 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9025,6 +9025,7 @@ public static partial class RuntimeFeature public static partial class RuntimeHelpers { public static int OffsetToStringData { get { throw null; } } + public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) { throw null; } public static void EnsureSufficientExecutionStack() { } public static new bool Equals(object? o1, object? o2) { throw null; } public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServices.RuntimeHelpers.TryCode code, System.Runtime.CompilerServices.RuntimeHelpers.CleanupCode backoutCode, object? userData) { } From a1b05072fc591cb6ca1119539d0255e57caf8551 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 22 Jan 2020 12:28:16 -0800 Subject: [PATCH 05/83] Add tests for RuntimeHelpers.AllocateTypeAssociatedMemory --- src/coreclr/src/CMakeLists.txt | 1 + .../src/dlls/mscoree/coreclr/CMakeLists.txt | 2 + src/coreclr/src/interop/CMakeLists.txt | 35 ++ src/coreclr/src/interop/comwrappers.cpp | 510 ++++++++++++++++++ src/coreclr/src/interop/comwrappers.h | 130 +++++ src/coreclr/src/interop/interoplib.h | 24 + src/coreclr/src/interop/platform.h | 26 + .../src/interop/referencetrackertypes.h | 59 ++ .../AllocateTypeAssociatedMemory.cs | 43 ++ .../ExecuteCodeWithGuaranteedCleanup.cs | 78 ++- .../RuntimeHelpers/RuntimeHelpersTests.cs | 24 + ...anup.csproj => RuntimeHelpersTests.csproj} | 4 +- 12 files changed, 891 insertions(+), 45 deletions(-) create mode 100644 src/coreclr/src/interop/CMakeLists.txt create mode 100644 src/coreclr/src/interop/comwrappers.cpp create mode 100644 src/coreclr/src/interop/comwrappers.h create mode 100644 src/coreclr/src/interop/interoplib.h create mode 100644 src/coreclr/src/interop/platform.h create mode 100644 src/coreclr/src/interop/referencetrackertypes.h create mode 100644 src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs create mode 100644 src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs rename src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/{ExecuteCodeWithGuaranteedCleanup.csproj => RuntimeHelpersTests.csproj} (65%) diff --git a/src/coreclr/src/CMakeLists.txt b/src/coreclr/src/CMakeLists.txt index 6a753253f20441..b6584b2de78301 100644 --- a/src/coreclr/src/CMakeLists.txt +++ b/src/coreclr/src/CMakeLists.txt @@ -74,6 +74,7 @@ add_subdirectory(tools) add_subdirectory(unwinder) add_subdirectory(ildasm) add_subdirectory(ilasm) +add_subdirectory(interop) if(CLR_CMAKE_HOST_UNIX) add_subdirectory(palrt) diff --git a/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt index 2fabad169942f8..8db14ec94b7a24 100644 --- a/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt @@ -105,6 +105,8 @@ set(CORECLR_LIBRARIES ildbsymlib utilcode libraries-native + v3binder + interop ) if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt new file mode 100644 index 00000000000000..c5471059a1b9b0 --- /dev/null +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -0,0 +1,35 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +#include_directories(BEFORE "../vm/${ARCH_SOURCES_DIR}") +#include_directories(BEFORE "../vm") +#include_directories(BEFORE "inc") + +set(INTEROP_COMMON_SOURCES +) + +set(INTEROP_COMMON_HEADERS + interoplib.h + platform.h +) + +set(INTEROP_SOURCES + ${INTEROP_COMMON_SOURCES} +) + +set(INTEROP_HEADERS + ${INTEROP_COMMON_HEADERS} +) + +if (WIN32) + list(APPEND INTEROP_SOURCES + ${INTEROP_HEADERS} + comwrappers.cpp + comwrappers.h + referencetrackertypes.h) +endif(WIN32) + +convert_to_absolute_path(INTEROP_SOURCES ${INTEROP_SOURCES}) + +add_library_clr(interop + STATIC + ${INTEROP_SOURCES} +) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp new file mode 100644 index 00000000000000..e555545c6788b7 --- /dev/null +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -0,0 +1,510 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "comwrappers.h" + +namespace ABI +{ + //--------------------------------------------------------------------------------- + // Dispatch section of the ManagedObjectWrapper (MOW) + // + // Within the dispatch section, the ManagedObjectWrapper itself is inserted at all 16 byte + // aligned location. This allows the simple masking of the any ComInterfaceDispatch* to get + // access to the ManagedObjectWrapper by masking the lower 4 bits. Below is a sketch of how + // the dispatch section would appear in a 32-bit process. + // + // 16 byte aligned Vtable + // +-----------+ + // | MOW this | + // +-----------+ +-----+ + // COM IP-->| VTable ptr|----------------------------->|slot1| + // +-----------+ +-----+ +-----+ + // | VTable ptr|---------->|slot1| |slot2| + // +-----------+ +-----+ + + + // | VTable ptr| | ....| | ... | + // +-----------+ + + + + + // | MOW this | |slotN| |slotN| + // + + +-----+ +-----+ + // | .... | + // +-----------+ + // + // A 16 byte alignment permits a ratio of 3:1 COM vtables to ManagedObjectWrapper 'this' + // pointers in 32-bit process, but in 64-bit process the mapping is unfortunately only 1:1. + // See the dispatch section building API below for an example of how indexing works. + //-------------------------------------------------------------------------------- + + struct ComInterfaceDispatch + { + const void* vtable; + }; + ABI_ASSERT(sizeof(ComInterfaceDispatch) == sizeof(void*)); + + const size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2. + const intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1); + ABI_ASSERT(sizeof(void*) < DispatchAlignmentThisPtr); + + const intptr_t AlignmentThisPtrMaxPadding = DispatchAlignmentThisPtr - sizeof(void*); + const size_t EntriesPerThisPtr = (DispatchAlignmentThisPtr / sizeof(void*)) - 1; + + // Check if the instance can dispatch according to the ABI. + bool IsAbleToDispatch(_In_ ComInterfaceDispatch* disp) + { + return (reinterpret_cast(disp) & DispatchThisPtrMask) != 0; + } + + // Given the number of dispatch entries compute the needed number of 'this' pointer entries. + constexpr size_t ComputeThisPtrForDispatchSection(_In_ size_t dispatchCount) + { + return (dispatchCount / ABI::EntriesPerThisPtr) + ((dispatchCount % ABI::EntriesPerThisPtr) == 0 ? 0 : 1); + } + + // Given a pointer and a padding allowance, attempt to find an offset into + // the memory that is properly aligned for the dispatch section. + char* AlignDispatchSection(_In_ char* section, _In_ intptr_t extraPadding) + { + _ASSERTE(section != nullptr); + + // If the dispatch section is not properly aligned by default, we use + // utilize the padding to make sure the dispatch section can be aligned. + while ((reinterpret_cast(section) % ABI::DispatchAlignmentThisPtr) != 0) + { + // Check if there is padding to attempt an alignment + if (extraPadding <= 0) + { + std::abort(); // [TODO] Replace + } + + extraPadding -= sizeof(void*); + +#ifdef _DEBUG + // Poison unused sections of the section + std::memset(section, 0xff, sizeof(void*)); +#endif + + section += sizeof(void*); + } + + return section; + } + + struct EntrySet + { + const ComInterfaceEntry* start; + int32_t count; + }; + + // Populate the dispatch section with the entry sets + ComInterfaceDispatch* PopulateDispatchSection( + _In_ void* thisPtr, + _In_ void* dispatchSection, + _In_ size_t entrySetCount, + _In_ const EntrySet* entrySets) + { + // Define dispatch section iterator + const void** currDisp = reinterpret_cast(dispatchSection); + + // Keep rolling count of dispatch entries + int32_t dispCount = 0; + + // Iterate over all interface entry sets + const EntrySet* curr = entrySets; + const EntrySet* end = entrySets + entrySetCount; + for (; curr != end; ++curr) + { + const ComInterfaceEntry* currEntry = curr->start; + int32_t entryCount = curr->count; + + // Update dispatch section with 'this' pointer and vtables + for (int32_t i = 0; i < entryCount; ++i, ++dispCount, ++currEntry) + { + // Insert the 'this' pointer at the appropriate locations + // e.g.: + // 32-bit | 64-bit + // (0 * 4) % 16 = 0 | (0 * 8) % 16 = 0 + // (1 * 4) % 16 = 4 | (1 * 8) % 16 = 8 + // (2 * 4) % 16 = 8 | (2 * 8) % 16 = 0 + // (3 * 4) % 16 = 12 | (3 * 8) % 16 = 8 + // (4 * 4) % 16 = 0 | (4 * 8) % 16 = 0 + // (5 * 4) % 16 = 4 | (5 * 8) % 16 = 8 + // + if (((dispCount * sizeof(void*)) % ABI::DispatchAlignmentThisPtr) == 0) + { + *currDisp++ = thisPtr; + ++dispCount; + } + + // Fill in the dispatch entry + *currDisp++ = currEntry->Vtable; + } + } + + return reinterpret_cast(dispatchSection); + } + + // Given the entry index, compute the dispatch index + ComInterfaceDispatch* IndexIntoDispatchSection(_In_ int32_t i, _In_ ComInterfaceDispatch* dispatches) + { + // Convert the supplied zero based index into what it represents as a count. + const size_t count = static_cast(i) + 1; + + // Based on the supplied count, compute how many previous 'this' pointers would be + // required in the dispatch section and add that to the supplied index to get the + // index into the dispatch section. + const size_t idx = ComputeThisPtrForDispatchSection(count) + i; + + ComInterfaceDispatch* disp = dispatches + idx; + _ASSERTE(IsAbleToDispatch(disp)); + return disp; + } + + // Given a dispatcher instance, return the associated ManagedObjectWrapper. + ManagedObjectWrapper* ToManagedObjectWrapper(_In_ ComInterfaceDispatch* disp) + { + _ASSERTE(disp != nullptr && disp->vtable != nullptr); + + intptr_t wrapperMaybe = reinterpret_cast(disp) & DispatchThisPtrMask; + return *reinterpret_cast(wrapperMaybe); + } +} + +namespace +{ + HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface( + _In_ ABI::ComInterfaceDispatch* disp, + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + return wrapper->QueryInterface(riid, ppvObject); + } + + ULONG STDMETHODCALLTYPE ManagedObjectWrapper_AddRef(_In_ ABI::ComInterfaceDispatch* disp) + { + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + return wrapper->AddRef(); + } + + ULONG STDMETHODCALLTYPE ManagedObjectWrapper_Release(_In_ ABI::ComInterfaceDispatch* disp) + { + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + return wrapper->Release(); + } + + // Hard-coded ManagedObjectWrapper IUnknown vtable + const struct + { + decltype(&ManagedObjectWrapper_QueryInterface) QueryInterface; + decltype(&ManagedObjectWrapper_AddRef) AddRef; + decltype(&ManagedObjectWrapper_Release) Release; + } ManagedObjectWrapper_IUnknownImpl { + &ManagedObjectWrapper_QueryInterface, + &ManagedObjectWrapper_AddRef, + &ManagedObjectWrapper_Release + }; + + static_assert(sizeof(ManagedObjectWrapper_IUnknownImpl) == (3 * sizeof(void*)), "Unexpected vtable size"); +} + +namespace InteropLib +{ + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) + { + _ASSERTE(fpQueryInterface != nullptr + && fpAddRef != nullptr + && fpRelease != nullptr); + + *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; + *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; + *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; + } +} + +namespace +{ + const int32_t TrackerRefShift = 32; + constexpr ULONGLONG TrackerRefCounter = ULONGLONG{ 1 } << TrackerRefShift; + + constexpr ULONG GetTrackerCount(_In_ ULONGLONG c) + { + return static_cast(c >> TrackerRefShift); + } + + ULONG STDMETHODCALLTYPE TrackerTarget_AddRefFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp) + { + _ASSERTE(disp != nullptr && disp->vtable != nullptr); + + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + + return wrapper->AddRefFromReferenceTracker(); + } + + ULONG STDMETHODCALLTYPE TrackerTarget_ReleaseFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp) + { + _ASSERTE(disp != nullptr && disp->vtable != nullptr); + + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + + return wrapper->ReleaseFromReferenceTracker(); + } + + HRESULT STDMETHODCALLTYPE TrackerTarget_Peg(_In_ ABI::ComInterfaceDispatch* disp) + { + _ASSERTE(disp != nullptr && disp->vtable != nullptr); + + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + + return wrapper->Peg(); + } + + HRESULT STDMETHODCALLTYPE TrackerTarget_Unpeg(_In_ ABI::ComInterfaceDispatch* disp) + { + _ASSERTE(disp != nullptr && disp->vtable != nullptr); + + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + + return wrapper->Unpeg(); + } + + // Hard-coded IReferenceTrackerTarget vtable + const struct + { + decltype(ManagedObjectWrapper_IUnknownImpl) IUnknownImpl; + decltype(&TrackerTarget_AddRefFromReferenceTracker) AddRefFromReferenceTracker; + decltype(&TrackerTarget_ReleaseFromReferenceTracker) ReleaseFromReferenceTracker; + decltype(&TrackerTarget_Peg) Peg; + decltype(&TrackerTarget_Unpeg) Unpeg; + } ManagedObjectWrapper_IReferenceTrackerTargetImpl { + ManagedObjectWrapper_IUnknownImpl, + &TrackerTarget_AddRefFromReferenceTracker, + &TrackerTarget_ReleaseFromReferenceTracker, + &TrackerTarget_Peg, + &TrackerTarget_Unpeg + }; + + static_assert(sizeof(ManagedObjectWrapper_IReferenceTrackerTargetImpl) == (7 * sizeof(void*)), "Unexpected vtable size"); +} + +ManagedObjectWrapper* ManagedObjectWrapper::MapIUnknownToWrapper(_In_ IUnknown* pUnk) +{ + _ASSERTE(pUnk != nullptr); + + // If the first Vtable entry is part of the ManagedObjectWrapper IUnknown impl, + // we know how to interpret the IUnknown. + void* firstEntryInVtable = *reinterpret_cast(pUnk); + if (firstEntryInVtable == ManagedObjectWrapper_IUnknownImpl.QueryInterface) + return nullptr; + + ABI::ComInterfaceDispatch* disp = reinterpret_cast(pUnk); + return ABI::ToManagedObjectWrapper(disp); +} + +ManagedObjectWrapper* ManagedObjectWrapper::Create( + _In_ CreateComInterfaceFlags flags, + _In_ void* gcHandleToObject, + _In_ int32_t userDefinedCount, + _In_ ComInterfaceEntry* userDefined) +{ + // Maximum number of runtime supplied vtables + ComInterfaceEntry runtimeDefined[4]; + int32_t runtimeDefinedCount = 0; + + // Check if the caller will provide the IUnknown table + if ((flags & CreateComInterfaceFlags::CallerDefinedIUnknown) == CreateComInterfaceFlags::None) + { + ComInterfaceEntry& curr = runtimeDefined[runtimeDefinedCount++]; + curr.IID = __uuidof(IUnknown); + curr.Vtable = &ManagedObjectWrapper_IUnknownImpl; + } + + // Check if the caller wants tracker support + if ((flags & CreateComInterfaceFlags::TrackerSupport) == CreateComInterfaceFlags::TrackerSupport) + { + ComInterfaceEntry& curr = runtimeDefined[runtimeDefinedCount++]; + curr.IID = __uuidof(IReferenceTrackerTarget); + curr.Vtable = &ManagedObjectWrapper_IReferenceTrackerTargetImpl; + } + + _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefined)); + + // Compute size for ManagedObjectWrapper instance + const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ComInterfaceEntry); + const size_t totalDefinedCount = static_cast(runtimeDefinedCount) + userDefinedCount; + + // Compute the total entry size of dispatch section + const size_t totalDispatchSectionCount = ABI::ComputeThisPtrForDispatchSection(totalDefinedCount) + totalDefinedCount; + const size_t totalDispatchSectionSize = totalDispatchSectionCount * sizeof(void*); + + // Allocate memory for the ManagedObjectWrapper + char* wrapperMem = (char*)0;// rt::Alloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding); [TODO] + + // Compute Runtime defined offset + char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); + + // Copy in runtime supplied COM interface entries + if (0 < runtimeDefinedCount) + std::memcpy(runtimeDefinedOffset, runtimeDefined, totalRuntimeDefinedSize); + + // Compute the dispatch section offset and ensure it is aligned + char* dispatchSectionOffset = runtimeDefinedOffset + totalRuntimeDefinedSize; + dispatchSectionOffset = ABI::AlignDispatchSection(dispatchSectionOffset, ABI::AlignmentThisPtrMaxPadding); + + // Define the sets for the tables to insert + const ABI::EntrySet AllEntries[] = + { + { runtimeDefined, runtimeDefinedCount }, + { userDefined, userDefinedCount } + }; + + ABI::ComInterfaceDispatch* dispSection = ABI::PopulateDispatchSection(wrapperMem, dispatchSectionOffset, ARRAYSIZE(AllEntries), AllEntries); + + ManagedObjectWrapper* wrappers = new (wrapperMem) ManagedObjectWrapper + { + flags, + gcHandleToObject, + runtimeDefinedCount, + runtimeDefined, + userDefinedCount, + userDefined, + dispSection + }; + + return wrappers; +} + +ManagedObjectWrapper::ManagedObjectWrapper( + _In_ CreateComInterfaceFlags flags, + _In_ void* gcHandleToObject, + _In_ int32_t runtimeDefinedCount, + _In_ const ComInterfaceEntry* runtimeDefined, + _In_ int32_t userDefinedCount, + _In_ const ComInterfaceEntry* userDefined, + _In_ ABI::ComInterfaceDispatch* dispatches) + : Target{ gcHandleToObject } + , _runtimeDefinedCount{ runtimeDefinedCount } + , _userDefinedCount{ userDefinedCount } + , _runtimeDefined{ runtimeDefined } + , _userDefined{ userDefined } + , _flags{ flags } + , _dispatches{ dispatches } +{ } + +ManagedObjectWrapper::~ManagedObjectWrapper() +{ + // rt::ReleaseGCHandle(Target); [TODO] +} + +void* ManagedObjectWrapper::As(_In_ REFIID riid) +{ + // Find target interface and return dispatcher or null if not found. + for (int32_t i = 0; i < _runtimeDefinedCount; ++i) + { + if (IsEqualGUID(_runtimeDefined[i].IID, riid)) + { + return ABI::IndexIntoDispatchSection(i, _dispatches); + } + } + + for (int32_t i = 0; i < _userDefinedCount; ++i) + { + if (IsEqualGUID(_userDefined[i].IID, riid)) + { + return ABI::IndexIntoDispatchSection(i + _runtimeDefinedCount, _dispatches); + } + } + + return nullptr; +} + +void* ManagedObjectWrapper::GetObjectGCHandle() const +{ + return Target; +} + +bool ManagedObjectWrapper::IsAlive() const +{ + return rt::IsGCHandleLive(GetObjectGCHandle()); +} + +bool ManagedObjectWrapper::IsSet(_In_ CreateCCWFlags flag) const +{ + return (_flags & flag) != CreateCCWFlags::None; +} + +ULONG ManagedObjectWrapper::AddRefFromReferenceTracker() +{ + LONGLONG prev; + LONGLONG curr; + do + { + prev = _refCount; + curr = prev + TrackerRefCounter; + } while (::InterlockedCompareExchange64(&_refCount, curr, prev) != prev); + + return GetTrackerCount(curr); +} + +ULONG ManagedObjectWrapper::ReleaseFromReferenceTracker() +{ + LONGLONG prev; + LONGLONG curr; + do + { + prev = _refCount; + curr = prev - TrackerRefCounter; + } while (::InterlockedCompareExchange64(&_refCount, curr, prev) != prev); + + return GetTrackerCount(curr); +} + +HRESULT ManagedObjectWrapper::Peg() +{ + return E_NOTIMPL; +} + +HRESULT ManagedObjectWrapper::Unpeg() +{ + return E_NOTIMPL; +} + +HRESULT ManagedObjectWrapper::QueryInterface( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) +{ + if (ppvObject == nullptr) + return E_POINTER; + + // Find target interface + *ppvObject = As(riid); + if (*ppvObject == nullptr) + return E_NOINTERFACE; + + (void)AddRef(); + return S_OK; +} + +ULONG ManagedObjectWrapper::AddRef(void) +{ + return (ULONG)::InterlockedIncrement64(&_refCount); +} + +ULONG ManagedObjectWrapper::Release(void) +{ + ULONG refCount = (ULONG)::InterlockedDecrement64(&_refCount); + if (refCount == 0) + { + // Manually trigger the destructor since placement + // new was used to allocate object. + this->~ManagedObjectWrapper(); + // rt::Free(this); [TODO] + } + + return refCount; +} \ No newline at end of file diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h new file mode 100644 index 00000000000000..58fdf32f51204c --- /dev/null +++ b/src/coreclr/src/interop/comwrappers.h @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _INTEROP_COMWRAPPERS_H_ +#define _INTEROP_COMWRAPPERS_H_ + +#include "platform.h" +#include "referencetrackertypes.h" +#include // COM interfaces + +enum class CreateComInterfaceFlags +{ + None = 0, + CallerDefinedIUnknown = 1, + IgnoreCache = 2, + TrackerSupport = 4, +}; + +DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlags); + +enum class CreateRCWFlags +{ + None = 0, + TrackerObject = 1, + IgnoreCache = 2, +}; + +DEFINE_ENUM_FLAG_OPERATORS(CreateRCWFlags); + +struct ComInterfaceEntry +{ + GUID IID; + const void* Vtable; +}; + +// Forward declaration +namespace ABI +{ + struct ComInterfaceDispatch; +} + +// Class for wrapping a managed object and projecting it in a non-managed environment +class ManagedObjectWrapper +{ +public: + void* Target; + +private: + const int32_t _runtimeDefinedCount; + const int32_t _userDefinedCount; + const ComInterfaceEntry* _runtimeDefined; + const ComInterfaceEntry* _userDefined; + ABI::ComInterfaceDispatch* _dispatches; + + LONGLONG _refCount = 1; + const CreateComInterfaceFlags _flags; + +public: // static + // Convert the IUnknown if the instance is a ManagedObjectWrapper into a ManagedObjectWrapper, otherwise null. + static ManagedObjectWrapper* MapIUnknownToWrapper(_In_ IUnknown* pUnk); + + // Create a ManagedObjectWrapper instance + static ManagedObjectWrapper* Create( + _In_ CreateComInterfaceFlags flags, + _In_ void* gcHandleToObject, + _In_ int32_t userDefinedCount, + _In_ ComInterfaceEntry* userDefined); + +private: + ManagedObjectWrapper( + _In_ CreateComInterfaceFlags flags, + _In_ void* gcHandleToObject, + _In_ int32_t runtimeDefinedCount, + _In_ const ComInterfaceEntry* runtimeDefined, + _In_ int32_t userDefinedCount, + _In_ const ComInterfaceEntry* userDefined, + _In_ ABI::ComInterfaceDispatch* dispatches); + +public: + ~ManagedObjectWrapper(); + + void* As(_In_ REFIID riid); + void* GetObjectGCHandle() const; + bool IsAlive() const; + bool IsSet(_In_ CreateComInterfaceFlags flag) const; + +public: // IReferenceTrackerTarget + ULONG AddRefFromReferenceTracker(); + ULONG ReleaseFromReferenceTracker(); + HRESULT Peg(); + HRESULT Unpeg(); + +public: // Lifetime + HRESULT QueryInterface( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR * __RPC_FAR * ppvObject); + ULONG AddRef(void); + ULONG Release(void); +}; + +// ABI contract. This below offset is assumed in managed code. +ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); + +// Class for connecting a native COM object to a managed object instance +class NativeCOMWrapperInstance +{ + void* _gcHandle; + IReferenceTracker* _trackerObject; + IAgileReference* _objectReference; + +public: + NativeCOMWrapperInstance(_In_ void* gcHandle, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); + ~NativeCOMWrapperInstance(); + + void* GetObjectGCHandle() const; + bool IsAlive() const; + + // Get the IReferenceTracker instance without going through the reference proxy. + IReferenceTracker* GetReferenceTrackerFast(); + + // Get a type instance of the desired type through the reference proxy. + template + HRESULT GetInstanceProxy(_Outptr_ T** t) + { + return _objectReference->Resolve(t); + } +}; + +#endif // _INTEROP_COMWRAPPERS_H_ \ No newline at end of file diff --git a/src/coreclr/src/interop/interoplib.h b/src/coreclr/src/interop/interoplib.h new file mode 100644 index 00000000000000..98499017ca3dec --- /dev/null +++ b/src/coreclr/src/interop/interoplib.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _INTEROP_INTEROPLIB_H_ +#define _INTEROP_INTEROPLIB_H_ + +namespace InteropLib +{ + +#ifdef _WIN32 + +// Get internal interop IUnknown dispatch pointers. +void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease); + +#endif // _WIN32 + +} + +#endif // _INTEROP_INTEROPLIB_H_ + diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h new file mode 100644 index 00000000000000..0795e79907cec3 --- /dev/null +++ b/src/coreclr/src/interop/platform.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _INTEROP_PLATFORM_H_ +#define _INTEROP_PLATFORM_H_ + +#include +#include + +#ifdef _WIN32 +#include +#endif // _WIN32 + +#define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") + +// BEGIN [TODO] Remove +#include +#include + +#ifndef _ASSERTE +#define _ASSERTE(x) assert((x)) +#endif +// END [TODO] Remove + +#endif // _INTEROP_PLATFORM_H_ \ No newline at end of file diff --git a/src/coreclr/src/interop/referencetrackertypes.h b/src/coreclr/src/interop/referencetrackertypes.h new file mode 100644 index 00000000000000..b9802a48fa113c --- /dev/null +++ b/src/coreclr/src/interop/referencetrackertypes.h @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _INTEROP_REFERENCETRACKERTYPES_H_ +#define _INTEROP_REFERENCETRACKERTYPES_H_ + +#include + +// Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ + +class DECLSPEC_UUID("64bd43f8-bfee-4ec4-b7eb-2935158dae21") IReferenceTrackerTarget : public IUnknown +{ +public: + STDMETHOD_(ULONG, AddRefFromReferenceTracker)() = 0; + STDMETHOD_(ULONG, ReleaseFromReferenceTracker)() = 0; + STDMETHOD(Peg)() = 0; + STDMETHOD(Unpeg)() = 0; +}; + +class DECLSPEC_UUID("29a71c6a-3c42-4416-a39d-e2825a07a773") IReferenceTrackerHost : public IUnknown +{ +public: + STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags) = 0; + STDMETHOD(ReleaseDisconnectedReferenceSources)() = 0; + STDMETHOD(NotifyEndOfReferenceTrackingOnThread)() = 0; + STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) = 0; + STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated) = 0; + STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated) = 0; +}; + +class DECLSPEC_UUID("3cf184b4-7ccb-4dda-8455-7e6ce99a3298") IReferenceTrackerManager : public IUnknown +{ +public: + STDMETHOD(ReferenceTrackingStarted)() = 0; + STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) = 0; + STDMETHOD(ReferenceTrackingCompleted)() = 0; + STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost *pCLRServices) = 0; +}; + +class DECLSPEC_UUID("04b3486c-4687-4229-8d14-505ab584dd88") IFindReferenceTargetsCallback : public IUnknown +{ +public: + STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) = 0; +}; + +class DECLSPEC_UUID("11d3b13a-180e-4789-a8be-7712882893e6") IReferenceTracker : public IUnknown +{ +public: + STDMETHOD(ConnectFromTrackerSource)() = 0; + STDMETHOD(DisconnectFromTrackerSource)() = 0; + STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback *pCallback) = 0; + STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager **ppTrackerManager) = 0; + STDMETHOD(AddRefFromTrackerSource)() = 0; + STDMETHOD(ReleaseFromTrackerSource)() = 0; + STDMETHOD(PegFromTrackerSource)() = 0; +}; + +#endif // _INTEROP_REFERENCETRACKERTYPES_H_ diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs new file mode 100644 index 00000000000000..860cef4fb3487a --- /dev/null +++ b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +using System; +using TestLibrary; +using System.Runtime.CompilerServices; + +class AllocateTypeAssociatedMemoryTest +{ + private static void ValidateInvalidArguments() + { + try + { + RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); + Assert.Fail("No exception on invalid type"); + } + catch (ArgumentException) + { + } + + try + { + RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), -1); + Assert.Fail("No exception on invalid size"); + } + catch (ArgumentOutOfRangeException) + { + } + } + + private static void ValidateSuccess() + { + IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); + Assert.AreNotEqual(memory, IntPtr.Zero); + } + + public static void Run() + { + ValidateInvalidArguments(); + ValidateSuccess(); + } +} diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.cs index 40b92b715f3160..faf6504252dcc3 100644 --- a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.cs +++ b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.cs @@ -5,57 +5,49 @@ using System; using System.Runtime.CompilerServices; -namespace GCD +class GCD { - /// - /// Summary description for Class1. - /// - class GCD + private int _val = -2; + private int _exitcode = -1; + public GCD() {} + public int GetExitCode(){ return _exitcode;} + public void g () { - private int _val = -2; - private int _exitcode = -1; - public GCD() {} - public int GetExitCode(){ return _exitcode;} - public void g () - { - throw new System.Exception("TryCode test"); - } - public void TryCode0 (object obj) - { - _val = (int)obj; - g(); - } - public void CleanupCode0 (object obj, bool excpThrown) + throw new System.Exception("TryCode test"); + } + public void TryCode0 (object obj) + { + _val = (int)obj; + g(); + } + public void CleanupCode0 (object obj, bool excpThrown) + { + if(excpThrown && ((int)obj == _val)) { - if(excpThrown && ((int)obj == _val)) - { - _exitcode = 100; - } + _exitcode = 100; } } +} - - class GCDTest +class ExecuteCodeWithGuaranteedCleanupTest +{ + public static void Run() { - /// - /// The main entry point for the application. - /// - static int Main(string[] args) - { - GCD gcd = new GCD(); - RuntimeHelpers.TryCode t = new RuntimeHelpers.TryCode(gcd.TryCode0); - RuntimeHelpers.CleanupCode c = new RuntimeHelpers.CleanupCode(gcd.CleanupCode0); - int val = 21; - try - { - RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(t, c, val); - } - catch (Exception Ex) - { + GCD gcd = new GCD(); + RuntimeHelpers.TryCode t = new RuntimeHelpers.TryCode(gcd.TryCode0); + RuntimeHelpers.CleanupCode c = new RuntimeHelpers.CleanupCode(gcd.CleanupCode0); + int val = 21; + try + { + RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(t, c, val); + } + catch (Exception Ex) + { - } + } - return gcd.GetExitCode(); - } + int res = gcd.GetExitCode(); + if (res != 100) + throw new Exception($"{nameof(ExecuteCodeWithGuaranteedCleanupTest)} failed. Result: {res}"); } } diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs new file mode 100644 index 00000000000000..4abc5097c8dec0 --- /dev/null +++ b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +using System; + +class RuntimeHelpersTests +{ + static int Main(string[] args) + { + try + { + ExecuteCodeWithGuaranteedCleanupTest.Run(); + AllocateTypeAssociatedMemoryTest.Run(); + } + catch (Exception e) + { + Console.WriteLine(e); + return 101; + } + + return 100; + } +} diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.csproj b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.csproj similarity index 65% rename from src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.csproj rename to src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.csproj index 2e8010d058d9ec..fa8ab39bb0ea0f 100644 --- a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.csproj +++ b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.csproj @@ -5,9 +5,9 @@ 1 - + - + From 39d5155fb75f1d931703564659438eceb72df561 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 23 Jan 2020 12:10:00 -0800 Subject: [PATCH 06/83] Update interop lib import/export API headers --- src/coreclr/src/interop/CMakeLists.txt | 7 +++---- src/coreclr/src/interop/comwrappers.cpp | 7 ++++--- src/coreclr/src/interop/{ => inc}/interoplib.h | 6 +++--- src/coreclr/src/interop/inc/interoplibimports.h | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 10 deletions(-) rename src/coreclr/src/interop/{ => inc}/interoplib.h (80%) create mode 100644 src/coreclr/src/interop/inc/interoplibimports.h diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index c5471059a1b9b0..c4fcf3718cdc8f 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -1,13 +1,12 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) -#include_directories(BEFORE "../vm/${ARCH_SOURCES_DIR}") -#include_directories(BEFORE "../vm") -#include_directories(BEFORE "inc") +include_directories(BEFORE inc) set(INTEROP_COMMON_SOURCES ) set(INTEROP_COMMON_HEADERS - interoplib.h + inc/interoplibimports.h + inc/interoplib.h platform.h ) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index e555545c6788b7..fbf9a2d22b21cc 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include #include "comwrappers.h" namespace ABI @@ -430,12 +431,12 @@ void* ManagedObjectWrapper::GetObjectGCHandle() const bool ManagedObjectWrapper::IsAlive() const { - return rt::IsGCHandleLive(GetObjectGCHandle()); + return true; // rt::IsGCHandleLive(GetObjectGCHandle()); [TODO] } -bool ManagedObjectWrapper::IsSet(_In_ CreateCCWFlags flag) const +bool ManagedObjectWrapper::IsSet(_In_ CreateComInterfaceFlags flag) const { - return (_flags & flag) != CreateCCWFlags::None; + return (_flags & flag) != CreateComInterfaceFlags::None; } ULONG ManagedObjectWrapper::AddRefFromReferenceTracker() diff --git a/src/coreclr/src/interop/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h similarity index 80% rename from src/coreclr/src/interop/interoplib.h rename to src/coreclr/src/interop/inc/interoplib.h index 98499017ca3dec..05d8f49b0290d3 100644 --- a/src/coreclr/src/interop/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#ifndef _INTEROP_INTEROPLIB_H_ -#define _INTEROP_INTEROPLIB_H_ +#ifndef _INTEROP_INC_INTEROPLIB_H_ +#define _INTEROP_INC_INTEROPLIB_H_ namespace InteropLib { @@ -20,5 +20,5 @@ void GetIUnknownImpl( } -#endif // _INTEROP_INTEROPLIB_H_ +#endif // _INTEROP_INC_INTEROPLIB_H_ diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h new file mode 100644 index 00000000000000..a264f9d023b166 --- /dev/null +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _INTEROP_INC_INTEROPLIBIMPORTS_H_ +#define _INTEROP_INC_INTEROPLIBIMPORTS_H_ + +namespace InteropLibImports +{ + +} + +#endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ + From 8e4e655151ccef4134ad30a9d684e033eb2dd275 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 23 Jan 2020 15:14:25 -0800 Subject: [PATCH 07/83] Implement the ComWrappers.GetIUnknownImpl() API --- .../Runtime/InteropServices/ComWrappers.cs | 16 +++++++-- src/coreclr/src/vm/CMakeLists.txt | 3 ++ src/coreclr/src/vm/ecalllist.h | 7 ++++ src/coreclr/src/vm/interoplibinterface.cpp | 32 +++++++++++++++++ src/coreclr/src/vm/interoplibinterface.h | 21 +++++++++++ src/coreclr/src/vm/mscorlib.cpp | 1 + .../ref/System.Runtime.InteropServices.cs | 35 +++++++++++++++++++ 7 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/coreclr/src/vm/interoplibinterface.cpp create mode 100644 src/coreclr/src/vm/interoplibinterface.h diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index b6ea53ca6e5b69..98604311b31581 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; namespace System.Runtime.InteropServices @@ -126,6 +127,10 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); + // Call to execute the abstract instance function + internal static unsafe ComInterfaceEntry* CallComputeVtables(ComWrappers instance, object obj, CreateComInterfaceFlags flags, out int count) + => instance.ComputeVtables(obj, flags, out count); + /// /// Get the currently registered managed object or creates a new managed object and registers it. /// @@ -145,6 +150,10 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// Returns a managed object associated with the supplied external COM object. protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); + // Call to execute the abstract instance function + internal static object CallCreateObject(ComWrappers instance, IntPtr externalComObject, CreateObjectFlags flags) + => instance.CreateObject(externalComObject, flags); + /// /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. /// @@ -165,8 +174,9 @@ public void RegisterForReferenceTrackerHost() /// Function pointer to Release. /// [TODO] Consider just returning a struct. Perhaps an Enum for input? protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) - { - throw new NotImplementedException(); - } + => GetIUnknownImplInternal(out fpQueryInterface, out fpAddRef, out fpRelease); + + [DllImport(RuntimeHelpers.QCall)] + private static extern void GetIUnknownImplInternal(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease); } } \ No newline at end of file diff --git a/src/coreclr/src/vm/CMakeLists.txt b/src/coreclr/src/vm/CMakeLists.txt index 43b9bc34519615..30471d40716370 100644 --- a/src/coreclr/src/vm/CMakeLists.txt +++ b/src/coreclr/src/vm/CMakeLists.txt @@ -3,6 +3,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # Needed due to the cmunged files being in the binary folders, the set(CMAKE_INCLUDE_CURRENT_DIR ON) is not enough include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${ARCH_SOURCES_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../interop/inc) add_definitions(-DUNICODE) add_definitions(-D_UNICODE) @@ -593,6 +594,7 @@ list(APPEND VM_SOURCES_WKS dispparammarshaler.cpp dwreport.cpp eventreporter.cpp + interoplibinterface.cpp mngstdinterfaces.cpp notifyexternals.cpp olecontexthelpers.cpp @@ -621,6 +623,7 @@ list(APPEND VM_HEADERS_WKS dispparammarshaler.h dwreport.h eventreporter.h + interoplibinterface.h mngstdinterfaces.h notifyexternals.h olecontexthelpers.h diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 39ea0d1d199f73..43b0faf28c3e4d 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -991,6 +991,10 @@ FCFuncStart(gWinRTTypeNameConverterFuncs) FCFuncElement("GetTypeFromWinRTTypeName", StubHelpers::WinRTTypeNameConverter__GetTypeFromWinRTTypeName) FCFuncEnd() +FCFuncStart(gComWrappersFuncs) + QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl) +FCFuncEnd() + #endif // FEATURE_COMINTEROP FCFuncStart(gMngdRefCustomMarshalerFuncs) @@ -1210,6 +1214,9 @@ FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs) FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CLRConfig", "System", gClrConfig) FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers) +#ifdef FEATURE_COMINTEROP +FCClassElement("ComWrappers", "System.Runtime.InteropServices", gComWrappersFuncs) +#endif // FEATURE_COMINTEROP FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs) FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp new file mode 100644 index 00000000000000..89c0c1c226a000 --- /dev/null +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "interoplibinterface.h" + +// Interop library exports/imports +#include +#include + +#ifdef FEATURE_COMINTEROP + +void QCALLTYPE ComWrappersNative::GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) +{ + QCALL_CONTRACT; + + _ASSERTE(fpQueryInterface != nullptr); + _ASSERTE(fpAddRef != nullptr); + _ASSERTE(fpRelease != nullptr); + + BEGIN_QCALL; + + InteropLib::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); + + END_QCALL; +} + +#endif // FEATURE_COMINTEROP \ No newline at end of file diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h new file mode 100644 index 00000000000000..f7e43f4af859e7 --- /dev/null +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// Interface between the VM and Interop library. +// + +#ifdef FEATURE_COMINTEROP + +// Native calls for the managed ComWrappers API +class ComWrappersNative +{ +public: + static void QCALLTYPE GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease); +}; + +#endif // FEATURE_COMINTEROP \ No newline at end of file diff --git a/src/coreclr/src/vm/mscorlib.cpp b/src/coreclr/src/vm/mscorlib.cpp index 40a1e14f58ff78..6cdd5d9e1a9d1e 100644 --- a/src/coreclr/src/vm/mscorlib.cpp +++ b/src/coreclr/src/vm/mscorlib.cpp @@ -67,6 +67,7 @@ #include "variant.h" #include "oavariant.h" #include "mngstdinterfaces.h" +#include "interoplibinterface.h" #endif // FEATURE_COMINTEROP #include "stubhelpers.h" diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index aee1cf868493ca..eab0228b112c7e 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -983,6 +983,41 @@ public sealed partial class VariantWrapper public VariantWrapper(object? obj) { } public object? WrappedObject { get { throw null; } } } + [System.FlagsAttribute] + public enum CreateComInterfaceFlags + { + None = 0, + CallerDefinedIUnknown = 1, + IgnoreCache = 2, + TrackerSupport = 4, + } + [System.FlagsAttribute] + public enum CreateObjectFlags + { + None = 0, + TrackerObject = 1, + IgnoreCache = 2, + } + [System.CLSCompliantAttribute(false)] + public abstract class ComWrappers + { + public struct ComInterfaceEntry + { + public System.Guid IID; + public System.IntPtr Vtable; + } + public struct ComInterfaceDispatch + { + public System.IntPtr vftbl; + public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class { throw null; } + } + public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } + protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); + public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } + protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags); + public void RegisterForReferenceTrackerHost() { } + protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } + } } namespace System.Runtime.InteropServices.ComTypes { From 1e88958a288dbf1a31a1fc2afecf1569d00f7cb9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 23 Jan 2020 15:15:34 -0800 Subject: [PATCH 08/83] Add project for testing ComWrappers API. Add test for the ComWrappers.GetIUnknownImpl() API. --- .../COM/ComWrappers/ComWrappersTests.csproj | 13 +++++ .../src/Interop/COM/ComWrappers/Program.cs | 55 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj new file mode 100644 index 00000000000000..e46ccfce4e3dd3 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj @@ -0,0 +1,13 @@ + + + Exe + + true + true + true + + + + + + diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs new file mode 100644 index 00000000000000..1f6e4c739bebcf --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace ComWrappersTests +{ + using System; + using System.IO; + using System.Runtime.InteropServices; + + using TestLibrary; + + class Program + { + class MyComWrappers : ComWrappers + { + protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + throw new NotImplementedException(); + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + throw new NotImplementedException(); + } + + public static void ValidateIUnknownImpls() + { + ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); + + Assert.AreNotEqual(fpQueryInteface, IntPtr.Zero); + Assert.AreNotEqual(fpAddRef, IntPtr.Zero); + Assert.AreNotEqual(fpRelease, IntPtr.Zero); + } + } + + static void ValidateIUnknownImpls() + => MyComWrappers.ValidateIUnknownImpls(); + + static int Main(string[] doNotUse) + { + try + { + ValidateIUnknownImpls(); + } + catch (Exception e) + { + Console.WriteLine($"Test Failure: {e}"); + return 101; + } + + return 100; + } + } +} From c375198d3a1513de5c3f7e622858d281d46ac5d9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 23 Jan 2020 17:12:17 -0800 Subject: [PATCH 09/83] Stub out remaining ComWrapper QCall APIs --- .../Runtime/InteropServices/ComWrappers.cs | 36 ++++++++++++---- src/coreclr/src/vm/ecalllist.h | 3 ++ src/coreclr/src/vm/interoplibinterface.cpp | 43 ++++++++++++++++++- src/coreclr/src/vm/interoplibinterface.h | 16 ++++++- 4 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 98604311b31581..ae99915de5e892 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -111,9 +111,16 @@ private struct ComInterfaceInstance /// The generated COM interface that can be passed outside the .NET runtime. public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { - throw new NotImplementedException(); + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + ComWrappers impl = this; + return GetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack.Create(ref impl), ObjectHandleOnStack.Create(ref instance), flags); } + [DllImport(RuntimeHelpers.QCall)] + private static extern IntPtr GetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack comWrappersImpl, ObjectHandleOnStack instance, CreateComInterfaceFlags flags); + /// /// Compute the desired VTables for respecting the values of . /// @@ -128,8 +135,8 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); // Call to execute the abstract instance function - internal static unsafe ComInterfaceEntry* CallComputeVtables(ComWrappers instance, object obj, CreateComInterfaceFlags flags, out int count) - => instance.ComputeVtables(obj, flags, out count); + internal static unsafe ComInterfaceEntry* CallComputeVtables(ComWrappers comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) + => comWrappersImpl.ComputeVtables(obj, flags, out count); /// /// Get the currently registered managed object or creates a new managed object and registers it. @@ -139,9 +146,19 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// Returns a managed object associated with the supplied external COM object. public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { - throw new NotImplementedException(); + if (externalComObject == IntPtr.Zero) + throw new ArgumentNullException(nameof(externalComObject)); + + ComWrappers impl = this; + object? retValue = null; + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref retValue)); + + return retValue!; } + [DllImport(RuntimeHelpers.QCall)] + private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack retValue); + /// /// Create a managed object for respecting the values of . /// @@ -151,8 +168,8 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object CallCreateObject(ComWrappers instance, IntPtr externalComObject, CreateObjectFlags flags) - => instance.CreateObject(externalComObject, flags); + internal static object CallCreateObject(ComWrappers comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags) + => comWrappersImpl.CreateObject(externalComObject, flags); /// /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. @@ -163,16 +180,19 @@ internal static object CallCreateObject(ComWrappers instance, IntPtr externalCom /// public void RegisterForReferenceTrackerHost() { - throw new NotImplementedException(); + ComWrappers impl = this; + RegisterForReferenceTrackerHostInternal(ObjectHandleOnStack.Create(ref impl)); } + [DllImport(RuntimeHelpers.QCall)] + private static extern void RegisterForReferenceTrackerHostInternal(ObjectHandleOnStack comWrappersImpl); + /// /// Get the runtime provided IUnknown implementation. /// /// Function pointer to QueryInterface. /// Function pointer to AddRef. /// Function pointer to Release. - /// [TODO] Consider just returning a struct. Perhaps an Enum for input? protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) => GetIUnknownImplInternal(out fpQueryInterface, out fpAddRef, out fpRelease); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 43b0faf28c3e4d..14859e4976dacc 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -993,6 +993,9 @@ FCFuncEnd() FCFuncStart(gComWrappersFuncs) QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl) + QCFuncElement("GetOrCreateComInterfaceForObjectInternal", ComWrappersNative::GetOrCreateComInterfaceForObject) + QCFuncElement("GetOrCreateObjectForComInstanceInternal", ComWrappersNative::GetOrCreateObjectForComInstance) + QCFuncElement("RegisterForReferenceTrackerHostInternal", ComWrappersNative::RegisterForReferenceTrackerHost) FCFuncEnd() #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 89c0c1c226a000..a7f16589ad5750 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -29,4 +29,45 @@ void QCALLTYPE ComWrappersNative::GetIUnknownImpl( END_QCALL; } -#endif // FEATURE_COMINTEROP \ No newline at end of file +void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( + _In_ QCall::ObjectHandleOnStack comWrappersImpl, + _In_ QCall::ObjectHandleOnStack instance, + _In_ INT32 flags) +{ + QCALL_CONTRACT; + + void* wrapper = nullptr; + + BEGIN_QCALL; + + END_QCALL; + + return wrapper; +} + +void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( + _In_ QCall::ObjectHandleOnStack comWrappersImpl, + _In_ void* externalComObject, + _In_ INT32 flags, + _Inout_ QCall::ObjectHandleOnStack retValue) +{ + QCALL_CONTRACT; + + _ASSERTE(externalComObject != nullptr); + + BEGIN_QCALL; + + END_QCALL; +} + +void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( + _In_ QCall::ObjectHandleOnStack comWrappersImpl) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + END_QCALL; +} + +#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index f7e43f4af859e7..3a9c5ab789fef7 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -16,6 +16,20 @@ class ComWrappersNative _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, _Out_ void** fpRelease); + + static void* QCALLTYPE GetOrCreateComInterfaceForObject( + _In_ QCall::ObjectHandleOnStack comWrappersImpl, + _In_ QCall::ObjectHandleOnStack instance, + _In_ INT32 flags); + + static void QCALLTYPE GetOrCreateObjectForComInstance( + _In_ QCall::ObjectHandleOnStack comWrappersImpl, + _In_ void* externalComObject, + _In_ INT32 flags, + _Inout_ QCall::ObjectHandleOnStack retValue); + + static void QCALLTYPE RegisterForReferenceTrackerHost( + _In_ QCall::ObjectHandleOnStack comWrappersImpl); }; -#endif // FEATURE_COMINTEROP \ No newline at end of file +#endif // FEATURE_COMINTEROP From 2b118c6df6772a2fb2e518eb87369df8bb44bcda Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 24 Jan 2020 12:39:05 -0800 Subject: [PATCH 10/83] Add pseudo implementation and test for ComWrappers.RegisterForReferenceTrackerHost(). --- src/coreclr/src/dlls/mscorrc/mscorrc.rc | 1 + src/coreclr/src/dlls/mscorrc/resource.h | 1 + src/coreclr/src/interop/comwrappers.cpp | 27 +++++-- src/coreclr/src/interop/comwrappers.h | 11 ++- src/coreclr/src/interop/inc/interoplib.h | 6 ++ .../src/interop/inc/interoplibimports.h | 6 ++ src/coreclr/src/vm/interoplibinterface.cpp | 74 ++++++++++++++++++- .../src/Interop/COM/ComWrappers/Program.cs | 20 +++++ 8 files changed, 131 insertions(+), 15 deletions(-) diff --git a/src/coreclr/src/dlls/mscorrc/mscorrc.rc b/src/coreclr/src/dlls/mscorrc/mscorrc.rc index 8a7410ef25b2d5..f7bd564e6909f2 100644 --- a/src/coreclr/src/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/src/dlls/mscorrc/mscorrc.rc @@ -579,6 +579,7 @@ BEGIN IDS_EE_ATTEMPT_TO_CREATE_GENERIC_CCW "Generic types cannot be marshaled to COM interface pointers." IDS_EE_ATTEMPT_TO_CREATE_NON_ABSTRACT_CCW "Types with non-abstract methods cannot be marshaled to COM interface pointers." IDS_EE_COMIMPORT_METHOD_NO_INTERFACE "Method '%1' in ComImport class '%2' must implement an interface method." + IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS "Attempt to update previously set default ReferenceTrackerHost callbacks." IDS_CLASSLOAD_BAD_METHOD_COUNT "Metadata method count does not match method enumeration length for type '%1' from assembly '%2'." IDS_CLASSLOAD_BAD_FIELD_COUNT "Metadata field count does not match field enumeration length for type '%1' from assembly '%2'." diff --git a/src/coreclr/src/dlls/mscorrc/resource.h b/src/coreclr/src/dlls/mscorrc/resource.h index 342c0570d493bb..20261b06f58a45 100644 --- a/src/coreclr/src/dlls/mscorrc/resource.h +++ b/src/coreclr/src/dlls/mscorrc/resource.h @@ -369,6 +369,7 @@ #define IDS_EE_COMIMPORT_METHOD_NO_INTERFACE 0x1aab #define IDS_EE_OUT_OF_MEMORY_WITHIN_RANGE 0x1aac #define IDS_EE_ARRAY_DIMENSIONS_EXCEEDED 0x1aad +#define IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS 0x1aae #define IDS_CLASSLOAD_MI_CANNOT_OVERRIDE 0x1ab3 #define IDS_CLASSLOAD_COLLECTIBLEFIXEDVTATTR 0x1ab6 diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index fbf9a2d22b21cc..ccd4a01fb7d13f 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include #include "comwrappers.h" +#include namespace ABI { @@ -222,6 +222,17 @@ namespace InteropLib *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; } + + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) // [TODO] + { + static OBJECTHANDLE g_objectHandle = nullptr; + + if (g_objectHandle != nullptr) + return false; + + g_objectHandle = objectHandle; + return true; + } } namespace @@ -309,7 +320,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::MapIUnknownToWrapper(_In_ IUnknown* ManagedObjectWrapper* ManagedObjectWrapper::Create( _In_ CreateComInterfaceFlags flags, - _In_ void* gcHandleToObject, + _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, _In_ ComInterfaceEntry* userDefined) { @@ -344,7 +355,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( const size_t totalDispatchSectionSize = totalDispatchSectionCount * sizeof(void*); // Allocate memory for the ManagedObjectWrapper - char* wrapperMem = (char*)0;// rt::Alloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding); [TODO] + char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding); // Compute Runtime defined offset char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); @@ -369,7 +380,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( ManagedObjectWrapper* wrappers = new (wrapperMem) ManagedObjectWrapper { flags, - gcHandleToObject, + objectHandle, runtimeDefinedCount, runtimeDefined, userDefinedCount, @@ -382,13 +393,13 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( ManagedObjectWrapper::ManagedObjectWrapper( _In_ CreateComInterfaceFlags flags, - _In_ void* gcHandleToObject, + _In_ OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, _In_ const ComInterfaceEntry* runtimeDefined, _In_ int32_t userDefinedCount, _In_ const ComInterfaceEntry* userDefined, _In_ ABI::ComInterfaceDispatch* dispatches) - : Target{ gcHandleToObject } + : Target{ objectHandle } , _runtimeDefinedCount{ runtimeDefinedCount } , _userDefinedCount{ userDefinedCount } , _runtimeDefined{ runtimeDefined } @@ -424,7 +435,7 @@ void* ManagedObjectWrapper::As(_In_ REFIID riid) return nullptr; } -void* ManagedObjectWrapper::GetObjectGCHandle() const +OBJECTHANDLE ManagedObjectWrapper::GetObjectHandle() const { return Target; } @@ -504,7 +515,7 @@ ULONG ManagedObjectWrapper::Release(void) // Manually trigger the destructor since placement // new was used to allocate object. this->~ManagedObjectWrapper(); - // rt::Free(this); [TODO] + InteropLibImports::MemFree(this); } return refCount; diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 58fdf32f51204c..e908fea29ca555 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -6,9 +6,12 @@ #define _INTEROP_COMWRAPPERS_H_ #include "platform.h" +#include #include "referencetrackertypes.h" #include // COM interfaces +using OBJECTHANDLE = InteropLib::OBJECTHANDLE; + enum class CreateComInterfaceFlags { None = 0, @@ -44,7 +47,7 @@ namespace ABI class ManagedObjectWrapper { public: - void* Target; + OBJECTHANDLE Target; private: const int32_t _runtimeDefinedCount; @@ -63,14 +66,14 @@ class ManagedObjectWrapper // Create a ManagedObjectWrapper instance static ManagedObjectWrapper* Create( _In_ CreateComInterfaceFlags flags, - _In_ void* gcHandleToObject, + _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, _In_ ComInterfaceEntry* userDefined); private: ManagedObjectWrapper( _In_ CreateComInterfaceFlags flags, - _In_ void* gcHandleToObject, + _In_ OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, _In_ const ComInterfaceEntry* runtimeDefined, _In_ int32_t userDefinedCount, @@ -81,7 +84,7 @@ class ManagedObjectWrapper ~ManagedObjectWrapper(); void* As(_In_ REFIID riid); - void* GetObjectGCHandle() const; + OBJECTHANDLE GetObjectHandle() const; bool IsAlive() const; bool IsSet(_In_ CreateComInterfaceFlags flag) const; diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 05d8f49b0290d3..1a2cb90a67ee13 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -7,6 +7,8 @@ namespace InteropLib { + using Ptr_OBJECTREF = void*; + using OBJECTHANDLE = void*; #ifdef _WIN32 @@ -16,6 +18,10 @@ void GetIUnknownImpl( _Out_ void** fpAddRef, _Out_ void** fpRelease); +// Register the default callback in the Reference Tracker Host scenario. +// Returns true if registration succeeded, otherwise false. +bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle); + #endif // _WIN32 } diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index a264f9d023b166..fa6512db430431 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -5,9 +5,15 @@ #ifndef _INTEROP_INC_INTEROPLIBIMPORTS_H_ #define _INTEROP_INC_INTEROPLIBIMPORTS_H_ +#include "interoplib.h" + namespace InteropLibImports { + // Allocate the given amount of memory. + void* MemAlloc(_In_ size_t sizeInBytes); + // Free the previously allocated memory. + void MemFree(_In_ void* mem); } #endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index a7f16589ad5750..680023d3b252c6 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -3,12 +3,30 @@ // See the LICENSE file in the project root for more information. #include "common.h" -#include "interoplibinterface.h" -// Interop library exports/imports -#include +// Interop library header #include +#include "interoplibinterface.h" + +namespace InteropLibImports +{ + void* MemAlloc(_In_ size_t sizeInBytes) + { + STANDARD_VM_CONTRACT; + + _ASSERTE(0 != sizeInBytes); + return ::malloc(sizeInBytes); + } + + void MemFree(_In_ void* mem) + { + STANDARD_VM_CONTRACT; + + ::free(mem); + } +} + #ifdef FEATURE_COMINTEROP void QCALLTYPE ComWrappersNative::GetIUnknownImpl( @@ -40,6 +58,31 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( BEGIN_QCALL; + // Switch to COOP mode to check if the object already + // has a wrapper in its syncblock. + { + GCX_COOP(); + + struct + { + OBJECTREF implRef; + OBJECTREF instRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + gc.instRef = ObjectToOBJECTREF(*instance.m_ppObject); + _ASSERTE(gc.implRef != NULL && gc.instRef != NULL); + + + // If it does, then return the + // existing wrapper. If the object doesn't, then compute the + // VTables for the type. + GCPROTECT_END(); + } + END_QCALL; return wrapper; @@ -65,8 +108,33 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( { QCALL_CONTRACT; + const HandleType implHandleType{ HNDTYPE_STRONG }; + OBJECTHANDLE implHandle; + BEGIN_QCALL; + // Enter cooperative mode to create the handle and store it + // for future use in the reference tracker host scenario. + { + GCX_COOP(); + + OBJECTREF implRef = NULL; + GCPROTECT_BEGIN(implRef); + + implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + _ASSERTE(implRef != NULL); + + implHandle = GetAppDomain()->CreateTypedHandle(implRef, implHandleType); + + if (!InteropLib::RegisterReferenceTrackerHostCallback(implHandle)) + { + DestroyHandleCommon(implHandle, implHandleType); + COMPlusThrow(kInvalidOperationException, IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS); + } + + GCPROTECT_END(); + } + END_QCALL; } diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 1f6e4c739bebcf..cec524bbaade17 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -37,11 +37,31 @@ public static void ValidateIUnknownImpls() static void ValidateIUnknownImpls() => MyComWrappers.ValidateIUnknownImpls(); + static void ValidateRegisterForReferenceTrackerHost() + { + var wrappers1 = new MyComWrappers(); + wrappers1.RegisterForReferenceTrackerHost(); + + Assert.Throws( + () => + { + wrappers1.RegisterForReferenceTrackerHost(); + }, "Should not be able to re-register for ReferenceTrackerHost"); + + var wrappers2 = new MyComWrappers(); + Assert.Throws( + () => + { + wrappers2.RegisterForReferenceTrackerHost(); + }, "Should not be able to reset for ReferenceTrackerHost"); + } + static int Main(string[] doNotUse) { try { ValidateIUnknownImpls(); + ValidateRegisterForReferenceTrackerHost(); } catch (Exception e) { From 705d02fe649baa0ce317ae9b89dca3eb8284762a Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 24 Jan 2020 15:21:38 -0800 Subject: [PATCH 11/83] Add and validate metasigs for invoking static methods in ComWrappers from within the runtime. --- .../Runtime/InteropServices/ComWrappers.cs | 2 +- src/coreclr/src/interop/inc/interoplib.h | 23 ++- .../src/interop/inc/interoplibimports.h | 10 + src/coreclr/src/vm/interoplibinterface.cpp | 185 ++++++++++++++++-- src/coreclr/src/vm/metasig.h | 2 + src/coreclr/src/vm/mscorlib.h | 10 +- 6 files changed, 209 insertions(+), 23 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index ae99915de5e892..69c91fe32be0e3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -135,7 +135,7 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); // Call to execute the abstract instance function - internal static unsafe ComInterfaceEntry* CallComputeVtables(ComWrappers comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) + internal static unsafe void* CallComputeVtables(ComWrappers comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) => comWrappersImpl.ComputeVtables(obj, flags, out count); /// diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 1a2cb90a67ee13..878b1e4478e273 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -12,15 +12,20 @@ namespace InteropLib #ifdef _WIN32 -// Get internal interop IUnknown dispatch pointers. -void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease); - -// Register the default callback in the Reference Tracker Host scenario. -// Returns true if registration succeeded, otherwise false. -bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle); + // Get internal interop IUnknown dispatch pointers. + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease); + + // Register the default callback in the Reference Tracker Host scenario. + // Returns true if registration succeeded, otherwise false. + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle); + + IUnknown* GetOrCreateComInterfaceForObject( + _In_ QCall::ObjectHandleOnStack comWrappersImpl, + _In_ QCall::ObjectHandleOnStack instance, + _In_ INT32 flags); #endif // _WIN32 diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index fa6512db430431..ac7cc836e7bfd3 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -14,6 +14,16 @@ namespace InteropLibImports // Free the previously allocated memory. void MemFree(_In_ void* mem); + + // Given a ComWrappers implementation, get or create + // an IReferenceTrackerTarget instance for the supplied + // external object. + HRESULT GetOrCreateTrackerTargetForExternal( + _In_ InteropLib::OBJECTHANDLE impl, + _In_ IUnknown* externalComObject, + _In_ INT32 externalObjectFlags, + _In_ INT32 trackerTargetFlags, + _Outptr_ IUnknown** trackerTarget) noexcept; } #endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 680023d3b252c6..43dae65905bcd6 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -9,6 +9,78 @@ #include "interoplibinterface.h" +// void ____() +// { +// CONTRACTL +// { +// THROWS; +// //NOTHROW; +// MODE_COOPERATIVE; +// // MODE_PREEMPTIVE; +// // MODE_ANY; +// // PRECONDITION(pSrc != NULL); +// } +// CONTRACTL_END; +// } + +namespace +{ + void* CallComputeVTables( + _In_ OBJECTREF impl, + _In_ OBJECTREF instance, + _In_ INT32 flags, + _Out_ DWORD* vtableCount) + { + CONTRACTL + { + THROWS; + MODE_COOPERATIVE; + PRECONDITION(impl != NULL); + PRECONDITION(instance != NULL); + PRECONDITION(vtableCount != NULL); + } + CONTRACTL_END; + + void* vtables = NULL; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); + DECLARE_ARGHOLDER_ARRAY(args, 4); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(impl); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(instance); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(vtableCount); + CALL_MANAGED_METHOD(vtables, void*, args); + + return vtables; + } + + OBJECTREF CallGetObject( + _In_ OBJECTREF impl, + _In_ IUnknown* externalComObject, + _In_ INT32 flags) + { + CONTRACTL + { + THROWS; + MODE_COOPERATIVE; + PRECONDITION(impl != NULL); + PRECONDITION(externalComObject != NULL); + } + CONTRACTL_END; + + OBJECTREF retObjRef = NULL; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); + DECLARE_ARGHOLDER_ARRAY(args, 3); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(impl); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); + + return retObjRef; + } +} + namespace InteropLibImports { void* MemAlloc(_In_ size_t sizeInBytes) @@ -25,6 +97,53 @@ namespace InteropLibImports ::free(mem); } + + HRESULT GetOrCreateTrackerTargetForExternal( + _In_ InteropLib::OBJECTHANDLE impl, + _In_ IUnknown* externalComObject, + _In_ INT32 externalObjectFlags, + _In_ INT32 trackerTargetFlags, + _Outptr_ IUnknown** trackerTarget) noexcept + { + CONTRACTL + { + NOTHROWS; + MODE_ANY; + PRECONDITION(impl != NULL); + PRECONDITION(externalComObject != NULL); + PRECONDITION(trackerTarget != NULL); + } + CONTRACTL_END; + + ::OBJECTHANDLE implHandle = static_cast<::OBJECTHANDLE>(impl); + + { + GCX_COOP(); + + struct + { + OBJECTREF implRef; + OBJECTREF newObjRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = ObjectFromHandle(implHandle); + _ASSERTE(gc.implRef != NULL); + + // + // Get wrapper for external object + // + + // + // Get wrapper for managed object + // + + GCPROTECT_END(); + } + + return S_OK; + } } #ifdef FEATURE_COMINTEROP @@ -36,9 +155,9 @@ void QCALLTYPE ComWrappersNative::GetIUnknownImpl( { QCALL_CONTRACT; - _ASSERTE(fpQueryInterface != nullptr); - _ASSERTE(fpAddRef != nullptr); - _ASSERTE(fpRelease != nullptr); + _ASSERTE(fpQueryInterface != NULL); + _ASSERTE(fpAddRef != NULL); + _ASSERTE(fpRelease != NULL); BEGIN_QCALL; @@ -54,7 +173,7 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( { QCALL_CONTRACT; - void* wrapper = nullptr; + void* wrapper = NULL; BEGIN_QCALL; @@ -69,17 +188,29 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( OBJECTREF instRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + _ASSERTE(gc.implRef != NULL); + + // + // Check the objects SyncBlock for an existing COM object + // + gc.instRef = ObjectToOBJECTREF(*instance.m_ppObject); - _ASSERTE(gc.implRef != NULL && gc.instRef != NULL); + _ASSERTE(gc.instRef != NULL); + // + // Compute VTables for the new existing COM object + // + + DWORD vtableCount; + void* vtables = CallComputeVTables(gc.implRef, gc.instRef, flags, &vtableCount); + + // + // + // - // If it does, then return the - // existing wrapper. If the object doesn't, then compute the - // VTables for the type. GCPROTECT_END(); } @@ -90,16 +221,48 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( _In_ QCall::ObjectHandleOnStack comWrappersImpl, - _In_ void* externalComObject, + _In_ void* ext, _In_ INT32 flags, _Inout_ QCall::ObjectHandleOnStack retValue) { QCALL_CONTRACT; - _ASSERTE(externalComObject != nullptr); + _ASSERTE(ext != NULL); BEGIN_QCALL; + HRESULT hr; + IUnknown* externalComObject = reinterpret_cast(ext); + + // Determine the true identity of the object + SafeComHolder identity = NULL; + hr = externalComObject->QueryInterface(IID_IUnknown, &identity); + _ASSERTE(hr == S_OK); + + // Switch to COOP mode in order to check if the the InteropLib already + // has an object for the external COM object or to create a new one. + { + GCX_COOP(); + + struct + { + OBJECTREF implRef; + OBJECTREF newObjRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + _ASSERTE(gc.implRef != NULL); + + gc.newObjectRef = CallGetObject(gc.implRef, identity, flags); + + // Set the return value + retValue.Set(gc.newObjectRef); + + GCPROTECT_END(); + } + END_QCALL; } diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 8699224e0f1175..27c1a2fa87666b 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -196,6 +196,8 @@ DEFINE_METASIG_T(SM(PtrTypeName_ArrType_RetVoid, P(g(TYPENAMENATIVE)) a(C(TYPE)) DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v)) DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) +DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) DEFINE_METASIG(SM(Int_Int_RetVoid, i i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 3ce6149ded554a..6c67e2dca36f5b 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -452,12 +452,18 @@ DEFINE_METHOD(ICUSTOM_MARSHALER, MARSHAL_NATIVE_TO_MANAGED,MarshalNativeToMan DEFINE_METHOD(ICUSTOM_MARSHALER, MARSHAL_MANAGED_TO_NATIVE,MarshalManagedToNative, IM_Obj_RetIntPtr) DEFINE_METHOD(ICUSTOM_MARSHALER, CLEANUP_NATIVE_DATA, CleanUpNativeData, IM_IntPtr_RetVoid) DEFINE_METHOD(ICUSTOM_MARSHALER, CLEANUP_MANAGED_DATA, CleanUpManagedData, IM_Obj_RetVoid) -DEFINE_METHOD(ICUSTOM_MARSHALER, GET_NATIVE_DATA_SIZE, GetNativeDataSize, IM_RetInt) +DEFINE_METHOD(ICUSTOM_MARSHALER, GET_NATIVE_DATA_SIZE, GetNativeDataSize, IM_RetInt) #ifdef FEATURE_COMINTEROP DEFINE_CLASS(ICUSTOM_QUERYINTERFACE, Interop, ICustomQueryInterface) -DEFINE_METHOD(ICUSTOM_QUERYINTERFACE, GET_INTERFACE, GetInterface, IM_RefGuid_OutIntPtr_RetCustomQueryInterfaceResult) +DEFINE_METHOD(ICUSTOM_QUERYINTERFACE, GET_INTERFACE, GetInterface, IM_RefGuid_OutIntPtr_RetCustomQueryInterfaceResult) DEFINE_CLASS(CUSTOMQUERYINTERFACERESULT, Interop, CustomQueryInterfaceResult) + +DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) +DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) +DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) +DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_RetObj) #endif //FEATURE_COMINTEROP DEFINE_CLASS(SERIALIZATION_INFO, Serialization, SerializationInfo) From 7ec6cc9010fb581863c9aeb4235cdf89d7b852c1 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 27 Jan 2020 17:56:06 -0800 Subject: [PATCH 12/83] Implement the runtime interation for managed object wrapper creation. Update InteropSyncBlockInfo to hold onto wrapper instance. --- src/coreclr/src/interop/comwrappers.cpp | 107 ++++++--- src/coreclr/src/interop/comwrappers.h | 2 +- src/coreclr/src/interop/inc/interoplib.h | 21 +- .../src/interop/inc/interoplibimports.h | 12 +- src/coreclr/src/vm/interoplibinterface.cpp | 223 ++++++++++++++---- src/coreclr/src/vm/syncblk.h | 34 +++ 6 files changed, 303 insertions(+), 96 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index ccd4a01fb7d13f..c164cc9d51073a 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -5,6 +5,8 @@ #include "comwrappers.h" #include +using AllocScenario = InteropLibImports::AllocScenario; + namespace ABI { //--------------------------------------------------------------------------------- @@ -207,34 +209,6 @@ namespace static_assert(sizeof(ManagedObjectWrapper_IUnknownImpl) == (3 * sizeof(void*)), "Unexpected vtable size"); } -namespace InteropLib -{ - void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease) - { - _ASSERTE(fpQueryInterface != nullptr - && fpAddRef != nullptr - && fpRelease != nullptr); - - *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; - *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; - *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; - } - - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) // [TODO] - { - static OBJECTHANDLE g_objectHandle = nullptr; - - if (g_objectHandle != nullptr) - return false; - - g_objectHandle = objectHandle; - return true; - } -} - namespace { const int32_t TrackerRefShift = 32; @@ -325,13 +299,13 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( _In_ ComInterfaceEntry* userDefined) { // Maximum number of runtime supplied vtables - ComInterfaceEntry runtimeDefined[4]; + ComInterfaceEntry runtimeDefinedLocal[4]; int32_t runtimeDefinedCount = 0; // Check if the caller will provide the IUnknown table if ((flags & CreateComInterfaceFlags::CallerDefinedIUnknown) == CreateComInterfaceFlags::None) { - ComInterfaceEntry& curr = runtimeDefined[runtimeDefinedCount++]; + ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IUnknown); curr.Vtable = &ManagedObjectWrapper_IUnknownImpl; } @@ -339,12 +313,12 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( // Check if the caller wants tracker support if ((flags & CreateComInterfaceFlags::TrackerSupport) == CreateComInterfaceFlags::TrackerSupport) { - ComInterfaceEntry& curr = runtimeDefined[runtimeDefinedCount++]; + ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IReferenceTrackerTarget); curr.Vtable = &ManagedObjectWrapper_IReferenceTrackerTargetImpl; } - _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefined)); + _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefinedLocal)); // Compute size for ManagedObjectWrapper instance const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ComInterfaceEntry); @@ -355,14 +329,20 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( const size_t totalDispatchSectionSize = totalDispatchSectionCount * sizeof(void*); // Allocate memory for the ManagedObjectWrapper - char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding); + char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding, AllocScenario::ManagedObjectWrapper); + if (wrapperMem == nullptr) + return nullptr; // OOM // Compute Runtime defined offset char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); // Copy in runtime supplied COM interface entries + ComInterfaceEntry* runtimeDefined = nullptr; if (0 < runtimeDefinedCount) - std::memcpy(runtimeDefinedOffset, runtimeDefined, totalRuntimeDefinedSize); + { + std::memcpy(runtimeDefinedOffset, runtimeDefinedLocal, totalRuntimeDefinedSize); + runtimeDefined = reinterpret_cast(runtimeDefinedOffset); + } // Compute the dispatch section offset and ensure it is aligned char* dispatchSectionOffset = runtimeDefinedOffset + totalRuntimeDefinedSize; @@ -410,7 +390,8 @@ ManagedObjectWrapper::ManagedObjectWrapper( ManagedObjectWrapper::~ManagedObjectWrapper() { - // rt::ReleaseGCHandle(Target); [TODO] + // Tell the runtime to separate the managed object from this wrapper. + InteropLibImports::SeparateObjectInstanceFromWrapper(Target, As(IID_IUnknown)); } void* ManagedObjectWrapper::As(_In_ REFIID riid) @@ -515,8 +496,60 @@ ULONG ManagedObjectWrapper::Release(void) // Manually trigger the destructor since placement // new was used to allocate object. this->~ManagedObjectWrapper(); - InteropLibImports::MemFree(this); + InteropLibImports::MemFree(this, AllocScenario::ManagedObjectWrapper); } return refCount; -} \ No newline at end of file +} + +namespace InteropLib +{ + HRESULT CreateComInterfaceForObject( + _In_ OBJECTHANDLE instance, + _In_ INT32 vtableCount, + _In_ void* vtablesRaw, + _In_ INT32 flagsRaw, + _Outptr_ IUnknown** comObject) + { + if (instance == nullptr || vtablesRaw == nullptr || vtableCount < 0) + return E_INVALIDARG; + + if (comObject == nullptr) + return E_POINTER; + + // Convert inputs to appropriate types. + auto flags = static_cast(flagsRaw); + auto vtables = static_cast(vtablesRaw); + ManagedObjectWrapper* mow = ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables); + if (mow == nullptr) + return E_OUTOFMEMORY; + + *comObject = static_cast(mow->As(IID_IUnknown)); + return S_OK; + } + + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) + { + _ASSERTE(fpQueryInterface != nullptr + && fpAddRef != nullptr + && fpRelease != nullptr); + + *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; + *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; + *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; + } + + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) // [TODO] + { + static OBJECTHANDLE g_objectHandle = nullptr; + + if (g_objectHandle != nullptr) + return false; + + g_objectHandle = objectHandle; + return true; + } +} diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index e908fea29ca555..70501831303d15 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -6,9 +6,9 @@ #define _INTEROP_COMWRAPPERS_H_ #include "platform.h" +#include // COM interfaces #include #include "referencetrackertypes.h" -#include // COM interfaces using OBJECTHANDLE = InteropLib::OBJECTHANDLE; diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 878b1e4478e273..91b59a382f29aa 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -12,20 +12,23 @@ namespace InteropLib #ifdef _WIN32 - // Get internal interop IUnknown dispatch pointers. - void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease); + // Create an IUnknown instance that represents the supplied managed object instance. + HRESULT CreateComInterfaceForObject( + _In_ OBJECTHANDLE instance, + _In_ INT32 vtableCount, + _In_ void* vtables, + _In_ INT32 flags, + _Outptr_ IUnknown** comObject); // Register the default callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle); - IUnknown* GetOrCreateComInterfaceForObject( - _In_ QCall::ObjectHandleOnStack comWrappersImpl, - _In_ QCall::ObjectHandleOnStack instance, - _In_ INT32 flags); + // Get internal interop IUnknown dispatch pointers. + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease); #endif // _WIN32 diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index ac7cc836e7bfd3..493a9cf0a3ef12 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -9,11 +9,19 @@ namespace InteropLibImports { + enum class AllocScenario + { + ManagedObjectWrapper, + }; + // Allocate the given amount of memory. - void* MemAlloc(_In_ size_t sizeInBytes); + void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario); // Free the previously allocated memory. - void MemFree(_In_ void* mem); + void MemFree(_In_ void* mem, _In_ AllocScenario scenario); + + // Separate Object instance from wrapper + void SeparateObjectInstanceFromWrapper(_In_ InteropLib::OBJECTHANDLE handle, _In_ void* wrapper); // Given a ComWrappers implementation, get or create // an IReferenceTrackerTarget instance for the supplied diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 43dae65905bcd6..57f940bbf8c95e 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -25,6 +25,8 @@ namespace { + const HandleType InstanceHandleType{ HNDTYPE_STRONG }; + void* CallComputeVTables( _In_ OBJECTREF impl, _In_ OBJECTREF instance, @@ -43,14 +45,27 @@ namespace void* vtables = NULL; + struct + { + OBJECTREF implRef; + OBJECTREF instRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = impl; + gc.instRef = instance; + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); DECLARE_ARGHOLDER_ARRAY(args, 4); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(impl); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(instance); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.implRef); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.instRef); args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); args[ARGNUM_3] = PTR_TO_ARGHOLDER(vtableCount); CALL_MANAGED_METHOD(vtables, void*, args); + GCPROTECT_END(); + return vtables; } @@ -68,36 +83,106 @@ namespace } CONTRACTL_END; - OBJECTREF retObjRef = NULL; + OBJECTREF retObjRef; + + struct + { + OBJECTREF implRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = impl; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); DECLARE_ARGHOLDER_ARRAY(args, 3); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(impl); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.implRef); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); + GCPROTECT_END(); + return retObjRef; } } namespace InteropLibImports { - void* MemAlloc(_In_ size_t sizeInBytes) + void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + MODE_ANY; + PRECONDITION(sizeInBytes != 0); + } + CONTRACTL_END; - _ASSERTE(0 != sizeInBytes); return ::malloc(sizeInBytes); } - void MemFree(_In_ void* mem) + void MemFree(_In_ void* mem, _In_ AllocScenario scenario) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + MODE_ANY; + PRECONDITION(mem != NULL); + } + CONTRACTL_END; ::free(mem); } + void SeparateObjectInstanceFromWrapper(_In_ InteropLib::OBJECTHANDLE handle, _In_ void* wrapper) + { + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + PRECONDITION(handle != NULL); + PRECONDITION(wrapper != NULL); + } + CONTRACTL_END; + + ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); + + { + GCX_COOP(); + + struct + { + OBJECTREF instObjRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.instObjRef = ObjectFromHandle(objectHandle); + _ASSERTE(gc.instObjRef != NULL); + + // Get access to the SyncBlock and InteropSyncBlockInfo. + // Ideally these checks wouldn't be needed because at this point + // the data structures should have already been created. + SyncBlock* syncBlock = gc.instObjRef->PassiveGetSyncBlock(); + if (syncBlock != NULL) + { + InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfoNoCreate(); + if (interopInfo != NULL) + { + // Separate the managed object from the supplied wrapper. If this + // separation fails, the object handle shouldn't be destroyed. + bool wasSeparated = interopInfo->TrySeparateFromManagedObjectComWrapper(wrapper); + _ASSERTE(wasSeparated); + if (wasSeparated) + DestroyHandleCommon(objectHandle, InstanceHandleType); + } + } + + GCPROTECT_END(); + } + } + HRESULT GetOrCreateTrackerTargetForExternal( _In_ InteropLib::OBJECTHANDLE impl, _In_ IUnknown* externalComObject, @@ -107,8 +192,8 @@ namespace InteropLibImports { CONTRACTL { - NOTHROWS; - MODE_ANY; + NOTHROW; + MODE_PREEMPTIVE; PRECONDITION(impl != NULL); PRECONDITION(externalComObject != NULL); PRECONDITION(trackerTarget != NULL); @@ -148,24 +233,6 @@ namespace InteropLibImports #ifdef FEATURE_COMINTEROP -void QCALLTYPE ComWrappersNative::GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease) -{ - QCALL_CONTRACT; - - _ASSERTE(fpQueryInterface != NULL); - _ASSERTE(fpAddRef != NULL); - _ASSERTE(fpRelease != NULL); - - BEGIN_QCALL; - - InteropLib::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); - - END_QCALL; -} - void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ QCall::ObjectHandleOnStack instance, @@ -173,6 +240,9 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( { QCALL_CONTRACT; + HRESULT hr; + + SafeComHolder newWrapper; void* wrapper = NULL; BEGIN_QCALL; @@ -190,32 +260,73 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); - _ASSERTE(gc.implRef != NULL); - - // - // Check the objects SyncBlock for an existing COM object - // - gc.instRef = ObjectToOBJECTREF(*instance.m_ppObject); _ASSERTE(gc.instRef != NULL); - // - // Compute VTables for the new existing COM object - // + // Check the object's SyncBlock for a managed object wrapper. + SyncBlock* syncBlock = gc.instRef->GetSyncBlock(); + InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); + + // Query the associated InteropSyncBlockInfo for an existing managed object wrapper. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { + // Get the supplied COM Wrappers implementation to request VTable computation. + gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + _ASSERTE(gc.implRef != NULL); - DWORD vtableCount; - void* vtables = CallComputeVTables(gc.implRef, gc.instRef, flags, &vtableCount); + // Compute VTables for the new existing COM object + // + // N.B. Calling to compute the associated VTables is perhaps early since no lock + // is taken. However, a key assumption here is that the returned memory will be + // idempotent for the same object. + DWORD vtableCount; + void* vtables = CallComputeVTables(gc.implRef, gc.instRef, flags, &vtableCount); + + // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { + OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); + + // Call the InteropLib and create the associated managed object wrapper. + hr = InteropLib::CreateComInterfaceForObject(instHandle, vtableCount, vtables, flags, &newWrapper); + if (FAILED(hr)) + { + DestroyHandleCommon(instHandle, InstanceHandleType); + COMPlusThrowHR(hr); + } + _ASSERTE(!newWrapper.IsNull()); + + // Try setting the newly created managed object wrapper on the InteropSyncBlockInfo. + if (!interopInfo->TrySetManagedObjectComWrapper(newWrapper)) + { + // If the managed object wrapper couldn't be set, then + // it should be possible to get the current one. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + UNREACHABLE(); + } + } + } - // - // - // + // Determine what to return. + if (!newWrapper.IsNull()) + { + // A new managed object wrapper was created, remove the object from the holder. + // No AddRef() here since the wrapper should be created with a reference. + wrapper = newWrapper.Extract(); + } + else + { + // An existing wrapper should have an AddRef() performed. + _ASSERTE(wrapper != NULL); + (void)static_cast(wrapper)->AddRef(); + } GCPROTECT_END(); } END_QCALL; + _ASSERTE(wrapper != NULL); return wrapper; } @@ -235,7 +346,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( IUnknown* externalComObject = reinterpret_cast(ext); // Determine the true identity of the object - SafeComHolder identity = NULL; + SafeComHolder identity; hr = externalComObject->QueryInterface(IID_IUnknown, &identity); _ASSERTE(hr == S_OK); @@ -255,10 +366,10 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); _ASSERTE(gc.implRef != NULL); - gc.newObjectRef = CallGetObject(gc.implRef, identity, flags); + gc.newObjRef = CallGetObject(gc.implRef, identity, flags); // Set the return value - retValue.Set(gc.newObjectRef); + retValue.Set(gc.newObjRef); GCPROTECT_END(); } @@ -301,4 +412,22 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( END_QCALL; } +void QCALLTYPE ComWrappersNative::GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) +{ + QCALL_CONTRACT; + + _ASSERTE(fpQueryInterface != NULL); + _ASSERTE(fpAddRef != NULL); + _ASSERTE(fpRelease != NULL); + + BEGIN_QCALL; + + InteropLib::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); + + END_QCALL; +} + #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/syncblk.h b/src/coreclr/src/vm/syncblk.h index f30b056d887623..bedb4430894494 100644 --- a/src/coreclr/src/vm/syncblk.h +++ b/src/coreclr/src/vm/syncblk.h @@ -789,6 +789,40 @@ class InteropSyncBlockInfo // instead. TADDR m_pRCW; #endif + +public: + bool TryGetManagedObjectComWrapper(_Out_ void** mocw) + { + LIMITED_METHOD_DAC_CONTRACT; + *mocw = m_managedObjectComWrapper; + return (*mocw != NULL); + } + +#ifndef DACCESS_COMPILE + bool TrySetManagedObjectComWrapper(_In_ void* mocw) + { + LIMITED_METHOD_CONTRACT; + + return (FastInterlockCompareExchangePointer( + &m_managedObjectComWrapper, + mocw, + NULL) == NULL); + } +#endif // !DACCESS_COMPILE + + bool TrySeparateFromManagedObjectComWrapper(_In_ void* mocw) + { + LIMITED_METHOD_DAC_CONTRACT; + + return (FastInterlockCompareExchangePointer( + &m_managedObjectComWrapper, + NULL, + mocw) == mocw); + } + +private: + // See InteropLib API for usage. + void* m_managedObjectComWrapper; #endif // FEATURE_COMINTEROP }; From 67949a72802065abd9ae2ef4d61068161437a09b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 27 Jan 2020 17:58:47 -0800 Subject: [PATCH 13/83] Write test for creation of managed object wrapper with tracker support. --- src/coreclr/src/interop/comwrappers.cpp | 21 +++- src/coreclr/src/interop/comwrappers.h | 2 +- .../src/interop/inc/interoplibimports.h | 4 +- src/coreclr/src/vm/interoplibinterface.cpp | 38 +------ .../src/Interop/COM/ComWrappers/Program.cs | 104 +++++++++++++++++- 5 files changed, 123 insertions(+), 46 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index c164cc9d51073a..2632fcc92aaeb5 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -388,12 +388,6 @@ ManagedObjectWrapper::ManagedObjectWrapper( , _dispatches{ dispatches } { } -ManagedObjectWrapper::~ManagedObjectWrapper() -{ - // Tell the runtime to separate the managed object from this wrapper. - InteropLibImports::SeparateObjectInstanceFromWrapper(Target, As(IID_IUnknown)); -} - void* ManagedObjectWrapper::As(_In_ REFIID riid) { // Find target interface and return dispatcher or null if not found. @@ -493,6 +487,21 @@ ULONG ManagedObjectWrapper::Release(void) ULONG refCount = (ULONG)::InterlockedDecrement64(&_refCount); if (refCount == 0) { + // [TODO] Instead of freeing this instance, it should be neutered (i.e. delete object handle). + // This means the associated SyncBlock always has this instance until it is finalized with + // the associated Object. + // Export New APIs: + // IsNeutered() + // TrySetObjectHandle() + // FreeManagedObjectWrapper() + // + + OBJECTHANDLE local = Target; + Target = NULL; + + // Tell the runtime to delete the managed object instance handle. + InteropLibImports::DeleteObjectInstanceHandle(local); + // Manually trigger the destructor since placement // new was used to allocate object. this->~ManagedObjectWrapper(); diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 70501831303d15..55d50b7570a393 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -81,7 +81,7 @@ class ManagedObjectWrapper _In_ ABI::ComInterfaceDispatch* dispatches); public: - ~ManagedObjectWrapper(); + ~ManagedObjectWrapper() = default; void* As(_In_ REFIID riid); OBJECTHANDLE GetObjectHandle() const; diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 493a9cf0a3ef12..f58317a0c8cc0d 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -20,8 +20,8 @@ namespace InteropLibImports // Free the previously allocated memory. void MemFree(_In_ void* mem, _In_ AllocScenario scenario); - // Separate Object instance from wrapper - void SeparateObjectInstanceFromWrapper(_In_ InteropLib::OBJECTHANDLE handle, _In_ void* wrapper); + // Delete Object instance handle + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle); // Given a ComWrappers implementation, get or create // an IReferenceTrackerTarget instance for the supplied diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 57f940bbf8c95e..c35e4203545357 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -135,52 +135,18 @@ namespace InteropLibImports ::free(mem); } - void SeparateObjectInstanceFromWrapper(_In_ InteropLib::OBJECTHANDLE handle, _In_ void* wrapper) + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) { CONTRACTL { NOTHROW; MODE_PREEMPTIVE; PRECONDITION(handle != NULL); - PRECONDITION(wrapper != NULL); } CONTRACTL_END; ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); - - { - GCX_COOP(); - - struct - { - OBJECTREF instObjRef; - } gc; - ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); - - gc.instObjRef = ObjectFromHandle(objectHandle); - _ASSERTE(gc.instObjRef != NULL); - - // Get access to the SyncBlock and InteropSyncBlockInfo. - // Ideally these checks wouldn't be needed because at this point - // the data structures should have already been created. - SyncBlock* syncBlock = gc.instObjRef->PassiveGetSyncBlock(); - if (syncBlock != NULL) - { - InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfoNoCreate(); - if (interopInfo != NULL) - { - // Separate the managed object from the supplied wrapper. If this - // separation fails, the object handle shouldn't be destroyed. - bool wasSeparated = interopInfo->TrySeparateFromManagedObjectComWrapper(wrapper); - _ASSERTE(wasSeparated); - if (wasSeparated) - DestroyHandleCommon(objectHandle, InstanceHandleType); - } - } - - GCPROTECT_END(); - } + DestroyHandleCommon(objectHandle, InstanceHandleType); } HRESULT GetOrCreateTrackerTargetForExternal( diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index cec524bbaade17..b8ea2d8f02cc17 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -6,17 +6,88 @@ namespace ComWrappersTests { using System; using System.IO; + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using TestLibrary; class Program { + [Guid("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09")] + interface ITest + { + void SetValue(int i); + } + + class Test : ITest + { + private int value = -1; + public void SetValue(int i) => this.value = i; + public int GetValue() => this.value; + } + + public struct IUnknownVtbl + { + public IntPtr QueryInterface; + public IntPtr AddRef; + public IntPtr Release; + } + + public struct ITestVtbl + { + public IUnknownVtbl IUnknownImpl; + public IntPtr SetValue; + + public delegate int _SetValue(IntPtr thisPtr, int i); + public static _SetValue pSetValue = new _SetValue(SetValueInternal); + + public static int SetValueInternal(IntPtr dispatchPtr, int i) + { + unsafe + { + try + { + ComWrappers.ComInterfaceDispatch.GetInstance((ComWrappers.ComInterfaceDispatch*)dispatchPtr).SetValue(i); + } + catch (Exception e) + { + return e.HResult; + } + } + return 0; // S_OK; + } + } + class MyComWrappers : ComWrappers { protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { - throw new NotImplementedException(); + Assert.IsTrue(obj is Test); + + IntPtr fpQueryInteface = default; + IntPtr fpAddRef = default; + IntPtr fpRelease = default; + ComWrappers.GetIUnknownImpl(out fpQueryInteface, out fpAddRef, out fpRelease); + + var vtbl = new ITestVtbl() + { + IUnknownImpl = new IUnknownVtbl() + { + QueryInterface = fpQueryInteface, + AddRef = fpAddRef, + Release = fpRelease + }, + SetValue = Marshal.GetFunctionPointerForDelegate(ITestVtbl.pSetValue) + }; + var vtblRaw = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ITestVtbl), sizeof(ITestVtbl)); + Marshal.StructureToPtr(vtbl, vtblRaw, false); + + var entryRaw = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ITestVtbl), sizeof(ComInterfaceEntry)); + entryRaw->IID = typeof(ITest).GUID; + entryRaw->Vtable = vtblRaw; + + count = 1; + return entryRaw; } protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) @@ -34,6 +105,36 @@ public static void ValidateIUnknownImpls() } } + static void ValidateComInterfaceCreation() + { + var testObj = new Test(); + + var wrappers = new MyComWrappers(); + + // Allocate a wrapper for the object + IntPtr comWrapper = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); + Assert.AreNotEqual(comWrapper, IntPtr.Zero); + + // Get a wrapper for an object and verify it is the same one. + IntPtr comWrapperMaybe = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); + Assert.AreEqual(comWrapper, comWrapperMaybe); + + // Release the wrapper + int count = Marshal.Release(comWrapper); + Assert.AreEqual(count, 1); + count = Marshal.Release(comWrapperMaybe); + Assert.AreEqual(count, 0); + + // Create a new wrapper + IntPtr comWrapperNew = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); + Assert.AreNotEqual(comWrapper, IntPtr.Zero); + Assert.AreNotEqual(comWrapperNew, comWrapper); + + // Release the new wrapper + count = Marshal.Release(comWrapperNew); + Assert.AreEqual(count, 0); + } + static void ValidateIUnknownImpls() => MyComWrappers.ValidateIUnknownImpls(); @@ -60,6 +161,7 @@ static int Main(string[] doNotUse) { try { + ValidateComInterfaceCreation(); ValidateIUnknownImpls(); ValidateRegisterForReferenceTrackerHost(); } From 4ed9275a2d962d00f4c30f95a38436a96c66fd40 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 28 Jan 2020 14:54:02 -0800 Subject: [PATCH 14/83] Make managed object wrappers persistent until the associated managed object is finalized. Remove the CreateComInterfaceFlags.IgnoreCache --- .../Runtime/InteropServices/ComWrappers.cs | 7 +- src/coreclr/src/interop/comwrappers.cpp | 103 ++++++++++++------ src/coreclr/src/interop/comwrappers.h | 21 ++-- src/coreclr/src/interop/inc/interoplib.h | 14 ++- src/coreclr/src/vm/interoplibinterface.cpp | 27 ++++- src/coreclr/src/vm/interoplibinterface.h | 2 + src/coreclr/src/vm/interoputil.cpp | 7 ++ src/coreclr/src/vm/syncblk.h | 10 -- .../ref/System.Runtime.InteropServices.cs | 3 +- 9 files changed, 124 insertions(+), 70 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 69c91fe32be0e3..2352151345661c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -20,17 +20,12 @@ public enum CreateComInterfaceFlags /// CallerDefinedIUnknown = 1, - /// - /// Ignore the internal cache when creating a instance. - /// - IgnoreCache = 2, - /// /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown /// and as such none should be supplied by the caller. /// - TrackerSupport = 4, + TrackerSupport = 2, } /// diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 2632fcc92aaeb5..edb1b6f2bd017a 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -357,7 +357,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( ABI::ComInterfaceDispatch* dispSection = ABI::PopulateDispatchSection(wrapperMem, dispatchSectionOffset, ARRAYSIZE(AllEntries), AllEntries); - ManagedObjectWrapper* wrappers = new (wrapperMem) ManagedObjectWrapper + ManagedObjectWrapper* wrapper = new (wrapperMem) ManagedObjectWrapper { flags, objectHandle, @@ -368,7 +368,17 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( dispSection }; - return wrappers; + return wrapper; +} + +void ManagedObjectWrapper::Destroy(_In_ ManagedObjectWrapper* wrapper) +{ + _ASSERTE(wrapper != nullptr); + + // Manually trigger the destructor since placement + // new was used to allocate the object. + wrapper->~ManagedObjectWrapper(); + InteropLibImports::MemFree(wrapper, AllocScenario::ManagedObjectWrapper); } ManagedObjectWrapper::ManagedObjectWrapper( @@ -379,14 +389,25 @@ ManagedObjectWrapper::ManagedObjectWrapper( _In_ int32_t userDefinedCount, _In_ const ComInterfaceEntry* userDefined, _In_ ABI::ComInterfaceDispatch* dispatches) - : Target{ objectHandle } + : Target{ nullptr } , _runtimeDefinedCount{ runtimeDefinedCount } , _userDefinedCount{ userDefinedCount } , _runtimeDefined{ runtimeDefined } , _userDefined{ userDefined } , _flags{ flags } , _dispatches{ dispatches } -{ } +{ + bool wasSet = TrySetObjectHandle(objectHandle); + _ASSERTE(wasSet); +} + + +ManagedObjectWrapper::~ManagedObjectWrapper() +{ + // If the target isn't null, then a managed object + // is going to leak. + _ASSERTE(Target == nullptr); +} void* ManagedObjectWrapper::As(_In_ REFIID riid) { @@ -410,14 +431,9 @@ void* ManagedObjectWrapper::As(_In_ REFIID riid) return nullptr; } -OBJECTHANDLE ManagedObjectWrapper::GetObjectHandle() const +bool ManagedObjectWrapper::TrySetObjectHandle(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current) { - return Target; -} - -bool ManagedObjectWrapper::IsAlive() const -{ - return true; // rt::IsGCHandleLive(GetObjectGCHandle()); [TODO] + return (::InterlockedCompareExchangePointer(&Target, objectHandle, current) == current); } bool ManagedObjectWrapper::IsSet(_In_ CreateComInterfaceFlags flag) const @@ -484,28 +500,16 @@ ULONG ManagedObjectWrapper::AddRef(void) ULONG ManagedObjectWrapper::Release(void) { + OBJECTHANDLE local = Target; ULONG refCount = (ULONG)::InterlockedDecrement64(&_refCount); if (refCount == 0) { - // [TODO] Instead of freeing this instance, it should be neutered (i.e. delete object handle). - // This means the associated SyncBlock always has this instance until it is finalized with - // the associated Object. - // Export New APIs: - // IsNeutered() - // TrySetObjectHandle() - // FreeManagedObjectWrapper() - // - - OBJECTHANDLE local = Target; - Target = NULL; + // Attempt to reset the target if its current value is the same. + // It is possible the wrapper is in the middle of being reactivated. + (void)TrySetObjectHandle(nullptr, local); // Tell the runtime to delete the managed object instance handle. InteropLibImports::DeleteObjectInstanceHandle(local); - - // Manually trigger the destructor since placement - // new was used to allocate object. - this->~ManagedObjectWrapper(); - InteropLibImports::MemFree(this, AllocScenario::ManagedObjectWrapper); } return refCount; @@ -513,12 +517,12 @@ ULONG ManagedObjectWrapper::Release(void) namespace InteropLib { - HRESULT CreateComInterfaceForObject( + HRESULT CreateComWrapperForObject( _In_ OBJECTHANDLE instance, _In_ INT32 vtableCount, _In_ void* vtablesRaw, _In_ INT32 flagsRaw, - _Outptr_ IUnknown** comObject) + _Outptr_ IUnknown** comObject) noexcept { if (instance == nullptr || vtablesRaw == nullptr || vtableCount < 0) return E_INVALIDARG; @@ -537,10 +541,21 @@ namespace InteropLib return S_OK; } + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] + { + static OBJECTHANDLE g_objectHandle = nullptr; + + if (g_objectHandle != nullptr) + return false; + + g_objectHandle = objectHandle; + return true; + } + void GetIUnknownImpl( _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, - _Out_ void** fpRelease) + _Out_ void** fpRelease) noexcept { _ASSERTE(fpQueryInterface != nullptr && fpAddRef != nullptr @@ -551,14 +566,30 @@ namespace InteropLib *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; } - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) // [TODO] + HRESULT EnsureActiveComWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept { - static OBJECTHANDLE g_objectHandle = nullptr; + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(wrapperMaybe); + if (wrapper == nullptr || handle == nullptr) + return E_INVALIDARG; - if (g_objectHandle != nullptr) - return false; + ULONG count = wrapper->AddRef(); + if (count == 1) + { + ::InterlockedExchangePointer(&wrapper->Target, handle); + return S_FALSE; + } - g_objectHandle = objectHandle; - return true; + return S_OK; + } + + void DestroyComWrapperForObject(_In_ void* wrapperMaybe) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); + + // This should never happen. + // A caller should not be destroying a wrapper without knowing if the wrapper is valid. + _ASSERTE(wrapper != nullptr); + + ManagedObjectWrapper::Destroy(wrapper); } } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 55d50b7570a393..e296ed33bc7472 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -16,8 +16,7 @@ enum class CreateComInterfaceFlags { None = 0, CallerDefinedIUnknown = 1, - IgnoreCache = 2, - TrackerSupport = 4, + TrackerSupport = 2, }; DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlags); @@ -70,6 +69,9 @@ class ManagedObjectWrapper _In_ int32_t userDefinedCount, _In_ ComInterfaceEntry* userDefined); + // Destroy the instance + static void Destroy(_In_ ManagedObjectWrapper* wrapper); + private: ManagedObjectWrapper( _In_ CreateComInterfaceFlags flags, @@ -80,12 +82,13 @@ class ManagedObjectWrapper _In_ const ComInterfaceEntry* userDefined, _In_ ABI::ComInterfaceDispatch* dispatches); + ~ManagedObjectWrapper(); + public: - ~ManagedObjectWrapper() = default; void* As(_In_ REFIID riid); - OBJECTHANDLE GetObjectHandle() const; - bool IsAlive() const; + // Attempt to set the target object handle based on an assumed current value. + bool TrySetObjectHandle(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current = nullptr); bool IsSet(_In_ CreateComInterfaceFlags flag) const; public: // IReferenceTrackerTarget @@ -106,15 +109,15 @@ class ManagedObjectWrapper ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); // Class for connecting a native COM object to a managed object instance -class NativeCOMWrapperInstance +class NativeComWrapperInstance { - void* _gcHandle; + OBJECTHANDLE _object; IReferenceTracker* _trackerObject; IAgileReference* _objectReference; public: - NativeCOMWrapperInstance(_In_ void* gcHandle, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); - ~NativeCOMWrapperInstance(); + NativeComWrapperInstance(_In_ OBJECTHANDLE* object, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); + ~NativeComWrapperInstance(); void* GetObjectGCHandle() const; bool IsAlive() const; diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 91b59a382f29aa..8eb498a5ef239e 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -13,22 +13,28 @@ namespace InteropLib #ifdef _WIN32 // Create an IUnknown instance that represents the supplied managed object instance. - HRESULT CreateComInterfaceForObject( + HRESULT CreateComWrapperForObject( _In_ OBJECTHANDLE instance, _In_ INT32 vtableCount, _In_ void* vtables, _In_ INT32 flags, - _Outptr_ IUnknown** comObject); + _Outptr_ IUnknown** comObject) noexcept; + + // Destroy the supplied COM wrapper + void DestroyComWrapperForObject(_In_ void* wrapper) noexcept; // Register the default callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle); + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept; // Get internal interop IUnknown dispatch pointers. void GetIUnknownImpl( _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, - _Out_ void** fpRelease); + _Out_ void** fpRelease) noexcept; + + // Ensure the wrapper is active and take an AddRef. + HRESULT EnsureActiveComWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; #endif // _WIN32 diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index c35e4203545357..888a0d75251e1a 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -254,7 +254,7 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); // Call the InteropLib and create the associated managed object wrapper. - hr = InteropLib::CreateComInterfaceForObject(instHandle, vtableCount, vtables, flags, &newWrapper); + hr = InteropLib::CreateComWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); if (FAILED(hr)) { DestroyHandleCommon(instHandle, InstanceHandleType); @@ -282,9 +282,17 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( } else { - // An existing wrapper should have an AddRef() performed. _ASSERTE(wrapper != NULL); - (void)static_cast(wrapper)->AddRef(); + // It is possible the supplied wrapper is no longer valid. If so, reactivate the + // wrapper with the object instance's new handle. If this reactivation + // wasn't needed, delete the handle. + OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); + hr = InteropLib::EnsureActiveComWrapperAndAddRef(static_cast(wrapper), instHandle); + if (hr != S_OK) + DestroyHandleCommon(instHandle, InstanceHandleType); + + if (FAILED(hr)) + COMPlusThrowHR(hr); } GCPROTECT_END(); @@ -396,4 +404,17 @@ void QCALLTYPE ComWrappersNative::GetIUnknownImpl( END_QCALL; } +void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) +{ + CONTRACTL + { + NOTHROW; + MODE_ANY; + PRECONDITION(wrapper != NULL); + } + CONTRACTL_END; + + InteropLib::DestroyComWrapperForObject(wrapper); +} + #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 3a9c5ab789fef7..9eed2943d8a728 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -30,6 +30,8 @@ class ComWrappersNative static void QCALLTYPE RegisterForReferenceTrackerHost( _In_ QCall::ObjectHandleOnStack comWrappersImpl); + + static void DestroyManagedObjectComWrapper(_In_ void* wrapper); }; #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoputil.cpp b/src/coreclr/src/vm/interoputil.cpp index 0ec23b624aede6..1b2bb73fbf5bec 100644 --- a/src/coreclr/src/vm/interoputil.cpp +++ b/src/coreclr/src/vm/interoputil.cpp @@ -25,6 +25,7 @@ #include "siginfo.hpp" #include "eemessagebox.h" #include "finalizerthread.h" +#include "interoplibinterface.h" #ifdef FEATURE_COMINTEROP #include "cominterfacemarshaler.h" @@ -1976,6 +1977,12 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) pInteropInfo->SetCCW(NULL); pCCW->Cleanup(); } + + void* mocw; + if (pInteropInfo->TryGetManagedObjectComWrapper(&mocw)) + { + ComWrappersNative::DestroyManagedObjectComWrapper(mocw); + } } void ReleaseRCWsInCachesNoThrow(LPVOID pCtxCookie) diff --git a/src/coreclr/src/vm/syncblk.h b/src/coreclr/src/vm/syncblk.h index bedb4430894494..05e2b0ecadfd80 100644 --- a/src/coreclr/src/vm/syncblk.h +++ b/src/coreclr/src/vm/syncblk.h @@ -810,16 +810,6 @@ class InteropSyncBlockInfo } #endif // !DACCESS_COMPILE - bool TrySeparateFromManagedObjectComWrapper(_In_ void* mocw) - { - LIMITED_METHOD_DAC_CONTRACT; - - return (FastInterlockCompareExchangePointer( - &m_managedObjectComWrapper, - NULL, - mocw) == mocw); - } - private: // See InteropLib API for usage. void* m_managedObjectComWrapper; diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index eab0228b112c7e..a5af9b45999ee2 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -988,8 +988,7 @@ public enum CreateComInterfaceFlags { None = 0, CallerDefinedIUnknown = 1, - IgnoreCache = 2, - TrackerSupport = 4, + TrackerSupport = 2, } [System.FlagsAttribute] public enum CreateObjectFlags From 9249f51013fcf15b09d7c52cc5a7ae7ed25af412 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 28 Jan 2020 19:04:07 -0800 Subject: [PATCH 15/83] Update export namespaces in InteropLib --- src/coreclr/src/interop/comwrappers.cpp | 123 +++++++++++---------- src/coreclr/src/interop/comwrappers.h | 6 +- src/coreclr/src/interop/inc/interoplib.h | 54 +++++---- src/coreclr/src/vm/interoplibinterface.cpp | 13 ++- 4 files changed, 104 insertions(+), 92 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index edb1b6f2bd017a..f2f8cead76931d 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -517,79 +517,82 @@ ULONG ManagedObjectWrapper::Release(void) namespace InteropLib { - HRESULT CreateComWrapperForObject( - _In_ OBJECTHANDLE instance, - _In_ INT32 vtableCount, - _In_ void* vtablesRaw, - _In_ INT32 flagsRaw, - _Outptr_ IUnknown** comObject) noexcept - { - if (instance == nullptr || vtablesRaw == nullptr || vtableCount < 0) - return E_INVALIDARG; - - if (comObject == nullptr) - return E_POINTER; + namespace Com + { + HRESULT CreateWrapperForObject( + _In_ OBJECTHANDLE instance, + _In_ INT32 vtableCount, + _In_ void* vtablesRaw, + _In_ INT32 flagsRaw, + _Outptr_ IUnknown** comObject) noexcept + { + if (instance == nullptr || vtablesRaw == nullptr || vtableCount < 0) + return E_INVALIDARG; - // Convert inputs to appropriate types. - auto flags = static_cast(flagsRaw); - auto vtables = static_cast(vtablesRaw); - ManagedObjectWrapper* mow = ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables); - if (mow == nullptr) - return E_OUTOFMEMORY; + if (comObject == nullptr) + return E_POINTER; - *comObject = static_cast(mow->As(IID_IUnknown)); - return S_OK; - } + // Convert inputs to appropriate types. + auto flags = static_cast(flagsRaw); + auto vtables = static_cast(vtablesRaw); + ManagedObjectWrapper* mow = ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables); + if (mow == nullptr) + return E_OUTOFMEMORY; - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] - { - static OBJECTHANDLE g_objectHandle = nullptr; + *comObject = static_cast(mow->As(IID_IUnknown)); + return S_OK; + } - if (g_objectHandle != nullptr) - return false; + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] + { + static OBJECTHANDLE g_objectHandle = nullptr; - g_objectHandle = objectHandle; - return true; - } + if (g_objectHandle != nullptr) + return false; - void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease) noexcept - { - _ASSERTE(fpQueryInterface != nullptr - && fpAddRef != nullptr - && fpRelease != nullptr); + g_objectHandle = objectHandle; + return true; + } - *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; - *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; - *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; - } + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) noexcept + { + _ASSERTE(fpQueryInterface != nullptr + && fpAddRef != nullptr + && fpRelease != nullptr); - HRESULT EnsureActiveComWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(wrapperMaybe); - if (wrapper == nullptr || handle == nullptr) - return E_INVALIDARG; + *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; + *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; + *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; + } - ULONG count = wrapper->AddRef(); - if (count == 1) + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept { - ::InterlockedExchangePointer(&wrapper->Target, handle); - return S_FALSE; - } + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(wrapperMaybe); + if (wrapper == nullptr || handle == nullptr) + return E_INVALIDARG; - return S_OK; - } + ULONG count = wrapper->AddRef(); + if (count == 1) + { + ::InterlockedExchangePointer(&wrapper->Target, handle); + return S_FALSE; + } - void DestroyComWrapperForObject(_In_ void* wrapperMaybe) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); + return S_OK; + } - // This should never happen. - // A caller should not be destroying a wrapper without knowing if the wrapper is valid. - _ASSERTE(wrapper != nullptr); + void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); - ManagedObjectWrapper::Destroy(wrapper); + // This should never happen. + // A caller should not be destroying a wrapper without knowing if the wrapper is valid. + _ASSERTE(wrapper != nullptr); + + ManagedObjectWrapper::Destroy(wrapper); + } } } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index e296ed33bc7472..49327364cb0f34 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -109,15 +109,15 @@ class ManagedObjectWrapper ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); // Class for connecting a native COM object to a managed object instance -class NativeComWrapperInstance +class NativeObjectWrapperInstance { OBJECTHANDLE _object; IReferenceTracker* _trackerObject; IAgileReference* _objectReference; public: - NativeComWrapperInstance(_In_ OBJECTHANDLE* object, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); - ~NativeComWrapperInstance(); + NativeObjectWrapperInstance(_In_ OBJECTHANDLE* object, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); + ~NativeObjectWrapperInstance(); void* GetObjectGCHandle() const; bool IsAlive() const; diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 8eb498a5ef239e..3b3dab15fd3d8d 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -7,34 +7,40 @@ namespace InteropLib { - using Ptr_OBJECTREF = void*; using OBJECTHANDLE = void*; #ifdef _WIN32 - // Create an IUnknown instance that represents the supplied managed object instance. - HRESULT CreateComWrapperForObject( - _In_ OBJECTHANDLE instance, - _In_ INT32 vtableCount, - _In_ void* vtables, - _In_ INT32 flags, - _Outptr_ IUnknown** comObject) noexcept; - - // Destroy the supplied COM wrapper - void DestroyComWrapperForObject(_In_ void* wrapper) noexcept; - - // Register the default callback in the Reference Tracker Host scenario. - // Returns true if registration succeeded, otherwise false. - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept; - - // Get internal interop IUnknown dispatch pointers. - void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease) noexcept; - - // Ensure the wrapper is active and take an AddRef. - HRESULT EnsureActiveComWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; + namespace Com + { + using NativeObjectWrapperHandle = void*; + + // Create an IUnknown instance that represents the supplied managed object instance. + HRESULT CreateWrapperForObject( + _In_ OBJECTHANDLE instance, + _In_ INT32 vtableCount, + _In_ void* vtables, + _In_ INT32 flags, + _Outptr_ IUnknown** comObject) noexcept; + + // Destroy the supplied COM wrapper + void DestroyWrapperForObject(_In_ void* wrapper) noexcept; + + // Register the default callback in the Reference Tracker Host scenario. + // Returns true if registration succeeded, otherwise false. + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept; + + // Get internal interop IUnknown dispatch pointers. + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) noexcept; + + // Ensure the wrapper is active and take an AddRef. + // S_OK - the wrapper is active and the OBJECTHANDLE wasn't needed. + // S_FALSE - the wrapper was inactive and the OBJECTHANDLE argument was used. + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; + } #endif // _WIN32 diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 888a0d75251e1a..30d751cd9b06e9 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -254,7 +254,7 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); // Call the InteropLib and create the associated managed object wrapper. - hr = InteropLib::CreateComWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); + hr = InteropLib::Com::CreateWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); if (FAILED(hr)) { DestroyHandleCommon(instHandle, InstanceHandleType); @@ -265,6 +265,9 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( // Try setting the newly created managed object wrapper on the InteropSyncBlockInfo. if (!interopInfo->TrySetManagedObjectComWrapper(newWrapper)) { + // The new wrapper couldn't be set which means a wrapper already exists. + newWrapper.Release(); + // If the managed object wrapper couldn't be set, then // it should be possible to get the current one. if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) @@ -287,7 +290,7 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( // wrapper with the object instance's new handle. If this reactivation // wasn't needed, delete the handle. OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); - hr = InteropLib::EnsureActiveComWrapperAndAddRef(static_cast(wrapper), instHandle); + hr = InteropLib::Com::EnsureActiveWrapperAndAddRef(static_cast(wrapper), instHandle); if (hr != S_OK) DestroyHandleCommon(instHandle, InstanceHandleType); @@ -374,7 +377,7 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( implHandle = GetAppDomain()->CreateTypedHandle(implRef, implHandleType); - if (!InteropLib::RegisterReferenceTrackerHostCallback(implHandle)) + if (!InteropLib::Com::RegisterReferenceTrackerHostCallback(implHandle)) { DestroyHandleCommon(implHandle, implHandleType); COMPlusThrow(kInvalidOperationException, IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS); @@ -399,7 +402,7 @@ void QCALLTYPE ComWrappersNative::GetIUnknownImpl( BEGIN_QCALL; - InteropLib::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); + InteropLib::Com::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); END_QCALL; } @@ -414,7 +417,7 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) } CONTRACTL_END; - InteropLib::DestroyComWrapperForObject(wrapper); + InteropLib::Com::DestroyWrapperForObject(wrapper); } #endif // FEATURE_COMINTEROP From 2a22388c0c779882073806093ff6ff9dea4ed12c Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 28 Jan 2020 19:24:20 -0800 Subject: [PATCH 16/83] Surround UNREACHABLE() with braces because of how it is defined. --- src/coreclr/src/inc/CrstTypes.def | 3 + src/coreclr/src/inc/crsttypes.h | 1 + src/coreclr/src/interop/CMakeLists.txt | 1 + src/coreclr/src/interop/agilereferences.cpp | 35 ++ src/coreclr/src/interop/comwrappers.cpp | 197 ++++++++++-- src/coreclr/src/interop/comwrappers.h | 129 +++++++- src/coreclr/src/interop/inc/interoplib.h | 25 +- .../src/interop/inc/interoplibimports.h | 12 +- src/coreclr/src/interop/platform.h | 15 +- src/coreclr/src/vm/interoplibinterface.cpp | 304 +++++++++++++++++- 10 files changed, 659 insertions(+), 63 deletions(-) create mode 100644 src/coreclr/src/interop/agilereferences.cpp diff --git a/src/coreclr/src/inc/CrstTypes.def b/src/coreclr/src/inc/CrstTypes.def index cf53f5bdd24ad0..5bbf1b2d5198eb 100644 --- a/src/coreclr/src/inc/CrstTypes.def +++ b/src/coreclr/src/inc/CrstTypes.def @@ -478,6 +478,9 @@ End Crst RCWCleanupList End +Crst ExternalObjectContextCache +End + Crst ReDacl End diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index 4bc0665602dbfe..fa223e5fd17324 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -385,6 +385,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstException", "CrstExecuteManLock", "CrstExecuteManRangeLock", + "CrstExternalObjectContextCache", "CrstFCall", "CrstFriendAccessCache", "CrstFuncPtrStubs", diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index c4fcf3718cdc8f..268724d9ebddfa 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -21,6 +21,7 @@ set(INTEROP_HEADERS if (WIN32) list(APPEND INTEROP_SOURCES ${INTEROP_HEADERS} + agilereferences.cpp comwrappers.cpp comwrappers.h referencetrackertypes.h) diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp new file mode 100644 index 00000000000000..92e454a2e1cad4 --- /dev/null +++ b/src/coreclr/src/interop/agilereferences.cpp @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "comwrappers.h" + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +#include + +#else +// Forward declare if OS verion is not set high enough. +enum AgileReferenceOptions +{ + AGILEREFERENCE_DEFAULT = 0, + AGILEREFERENCE_DELAYEDMARSHAL = 1, +}; + +WINOLEAPI RoGetAgileReference( + _In_ enum AgileReferenceOptions options, + _In_ REFIID riid, + _In_ IUnknown* pUnk, + _COM_Outptr_ IAgileReference** ppAgileReference + ); + +#endif + +template<> +HRESULT CreateAgileReference( + _In_ IUnknown* object, + _Outptr_ IAgileReference** agileReference) +{ + // [TODO] Handle this on pre-Windows 8.1 plaforms + _ASSERTE(object != nullptr && agileReference != nullptr); + return ::RoGetAgileReference(AGILEREFERENCE_DEFAULT, __uuidof(object), object, agileReference); +} diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index f2f8cead76931d..aaa14d027df121 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -74,9 +74,7 @@ namespace ABI { // Check if there is padding to attempt an alignment if (extraPadding <= 0) - { - std::abort(); // [TODO] Replace - } + return nullptr; extraPadding -= sizeof(void*); @@ -292,12 +290,15 @@ ManagedObjectWrapper* ManagedObjectWrapper::MapIUnknownToWrapper(_In_ IUnknown* return ABI::ToManagedObjectWrapper(disp); } -ManagedObjectWrapper* ManagedObjectWrapper::Create( +HRESULT ManagedObjectWrapper::Create( _In_ CreateComInterfaceFlags flags, _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, - _In_ ComInterfaceEntry* userDefined) + _In_ ComInterfaceEntry* userDefined, + _Outptr_ ManagedObjectWrapper** mow) { + _ASSERTE(objectHandle != nullptr && mow != nullptr); + // Maximum number of runtime supplied vtables ComInterfaceEntry runtimeDefinedLocal[4]; int32_t runtimeDefinedCount = 0; @@ -331,7 +332,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( // Allocate memory for the ManagedObjectWrapper char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding, AllocScenario::ManagedObjectWrapper); if (wrapperMem == nullptr) - return nullptr; // OOM + return E_OUTOFMEMORY; // Compute Runtime defined offset char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); @@ -347,6 +348,8 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( // Compute the dispatch section offset and ensure it is aligned char* dispatchSectionOffset = runtimeDefinedOffset + totalRuntimeDefinedSize; dispatchSectionOffset = ABI::AlignDispatchSection(dispatchSectionOffset, ABI::AlignmentThisPtrMaxPadding); + if (dispatchSectionOffset == nullptr) + return E_UNEXPECTED; // Define the sets for the tables to insert const ABI::EntrySet AllEntries[] = @@ -368,7 +371,8 @@ ManagedObjectWrapper* ManagedObjectWrapper::Create( dispSection }; - return wrapper; + *mow = wrapper; + return S_OK; } void ManagedObjectWrapper::Destroy(_In_ ManagedObjectWrapper* wrapper) @@ -515,6 +519,110 @@ ULONG ManagedObjectWrapper::Release(void) return refCount; } +namespace +{ + const size_t ContextSentinal = 0x0a110ced; +} + +NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_In_ void* cxtMaybe) +{ + _ASSERTE(cxtMaybe != nullptr); + + // Convert the supplied context + char* cxtRaw = reinterpret_cast(cxtMaybe); + cxtRaw -= sizeof(NativeObjectWrapperContext); + NativeObjectWrapperContext* cxt = reinterpret_cast(cxtRaw); + +#ifdef _DEBUG + _ASSERTE(cxt->_sentinal == ContextSentinal); +#endif + + return cxt; +} + +HRESULT NativeObjectWrapperContext::Create( + _In_ IUnknown* external, + _In_ CreateObjectFlags flags, + _In_ size_t runtimeContextSize, + _Outptr_ NativeObjectWrapperContext** context) +{ + _ASSERTE(external != nullptr && context != nullptr); + + HRESULT hr; + + ComHolder trackerObject; + if ((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject) + { + hr = external->QueryInterface(&trackerObject); + if (SUCCEEDED(hr)) + { + // [TODO] RETURN_IF_FAILED(TrackerRCWManager::OnIReferenceTrackerFound(trackerObject)); + } + } + + ComHolder reference; + RETURN_IF_FAILED(CreateAgileReference(external, &reference)); + + // Allocate memory for the RCW + char* cxtMem = (char*)InteropLibImports::MemAlloc(sizeof(NativeObjectWrapperContext) + runtimeContextSize, AllocScenario::NativeObjectWrapper); + if (cxtMem == nullptr) + return E_OUTOFMEMORY; + + void* runtimeContext = cxtMem + sizeof(NativeObjectWrapperContext); + + // Contract specifically requires zeroing out runtime context. + std::memset(runtimeContext, 0, runtimeContextSize); + + NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ trackerObject, reference, runtimeContext }; + + if (contextLocal->GetReferenceTrackerFast() != nullptr) + { + // Inform the tracker object manager + _ASSERTE((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject); + // [TODO] TrackerRCWManager::AfterRCWCreated(contextLocal); + } + + *context = contextLocal; + return S_OK; +} + +void NativeObjectWrapperContext::Destroy(_In_ NativeObjectWrapperContext* wrapper) +{ + _ASSERTE(wrapper != nullptr); + + // Manually trigger the destructor since placement + // new was used to allocate the object. + wrapper->~NativeObjectWrapperContext(); + InteropLibImports::MemFree(wrapper, AllocScenario::NativeObjectWrapper); +} + +NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference, _In_ void* runtimeContext) + : _trackerObject{ trackerObject } + , _objectReference{ reference } + , _runtimeContext{ runtimeContext } +#ifdef _DEBUG + , _sentinal{ ContextSentinal } +#endif +{ + (void)_objectReference->AddRef(); +} + +NativeObjectWrapperContext::~NativeObjectWrapperContext() +{ + _trackerObject = nullptr; + (void)_objectReference->Release(); +} + +void* NativeObjectWrapperContext::GetRuntimeContext() const +{ + return _runtimeContext; +} + +IReferenceTracker* NativeObjectWrapperContext::GetReferenceTrackerFast() const +{ + return _trackerObject; +} + namespace InteropLib { namespace Com @@ -524,26 +632,74 @@ namespace InteropLib _In_ INT32 vtableCount, _In_ void* vtablesRaw, _In_ INT32 flagsRaw, - _Outptr_ IUnknown** comObject) noexcept + _Outptr_ IUnknown** wrapper) noexcept { - if (instance == nullptr || vtablesRaw == nullptr || vtableCount < 0) + if (instance == nullptr || (vtablesRaw == nullptr && vtableCount != 0) || vtableCount < 0) return E_INVALIDARG; - if (comObject == nullptr) + if (wrapper == nullptr) return E_POINTER; + HRESULT hr; + // Convert inputs to appropriate types. auto flags = static_cast(flagsRaw); auto vtables = static_cast(vtablesRaw); - ManagedObjectWrapper* mow = ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables); - if (mow == nullptr) - return E_OUTOFMEMORY; - *comObject = static_cast(mow->As(IID_IUnknown)); + ManagedObjectWrapper* mow; + RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow)); + + *wrapper = static_cast(mow->As(IID_IUnknown)); return S_OK; } - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] + void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); + + // This should never happen. + // A caller should not be destroying a wrapper without knowing if the wrapper is valid. + _ASSERTE(wrapper != nullptr); + + ManagedObjectWrapper::Destroy(wrapper); + } + + HRESULT CreateWrapperForExternal( + _In_ IUnknown* external, + _In_ INT32 flagsRaw, + _In_ size_t contextSize, + _Outptr_ void** context) noexcept + { + if (external == nullptr) + return E_INVALIDARG; + + if (context == nullptr) + return E_POINTER; + + HRESULT hr; + + // Convert input to appropriate type. + auto flags = static_cast(flagsRaw); + + NativeObjectWrapperContext* wrapperContext; + RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); + + *context = wrapperContext->GetRuntimeContext(); + return S_OK; + } + + void DestroyWrapperForExternal(_In_ void* contextMaybe) noexcept + { + NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); + + // This should never happen. + // A caller should not be destroying a context without knowing if the context is valid. + _ASSERTE(context != nullptr); + + NativeObjectWrapperContext::Destroy(context); + } + + bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] Move to tracker object manager { static OBJECTHANDLE g_objectHandle = nullptr; @@ -583,16 +739,5 @@ namespace InteropLib return S_OK; } - - void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); - - // This should never happen. - // A caller should not be destroying a wrapper without knowing if the wrapper is valid. - _ASSERTE(wrapper != nullptr); - - ManagedObjectWrapper::Destroy(wrapper); - } } } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 49327364cb0f34..77daabd51863cf 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -21,14 +21,14 @@ enum class CreateComInterfaceFlags DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlags); -enum class CreateRCWFlags +enum class CreateObjectFlags { None = 0, TrackerObject = 1, IgnoreCache = 2, }; -DEFINE_ENUM_FLAG_OPERATORS(CreateRCWFlags); +DEFINE_ENUM_FLAG_OPERATORS(CreateObjectFlags); struct ComInterfaceEntry { @@ -63,11 +63,12 @@ class ManagedObjectWrapper static ManagedObjectWrapper* MapIUnknownToWrapper(_In_ IUnknown* pUnk); // Create a ManagedObjectWrapper instance - static ManagedObjectWrapper* Create( + static HRESULT Create( _In_ CreateComInterfaceFlags flags, _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, - _In_ ComInterfaceEntry* userDefined); + _In_ ComInterfaceEntry* userDefined, + _Outptr_ ManagedObjectWrapper** mow); // Destroy the instance static void Destroy(_In_ ManagedObjectWrapper* wrapper); @@ -109,21 +110,40 @@ class ManagedObjectWrapper ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); // Class for connecting a native COM object to a managed object instance -class NativeObjectWrapperInstance +class NativeObjectWrapperContext { - OBJECTHANDLE _object; +#ifdef _DEBUG + const size_t _sentinal; +#endif + IReferenceTracker* _trackerObject; IAgileReference* _objectReference; + void* _runtimeContext; -public: - NativeObjectWrapperInstance(_In_ OBJECTHANDLE* object, _In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference); - ~NativeObjectWrapperInstance(); +public: // static + // Convert a context pointer into a NativeObjectWrapperContext. + static NativeObjectWrapperContext* MapFromRuntimeContext(_In_ void* cxt); - void* GetObjectGCHandle() const; - bool IsAlive() const; + // Create a NativeObjectWrapperContext instance + static HRESULT NativeObjectWrapperContext::Create( + _In_ IUnknown* external, + _In_ CreateObjectFlags flags, + _In_ size_t runtimeContextSize, + _Outptr_ NativeObjectWrapperContext** context); + + // Destroy the instance + static void Destroy(_In_ NativeObjectWrapperContext* wrapper); + +private: + NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference, _In_ void* runtimeContext); + ~NativeObjectWrapperContext(); + +public: + // Get the associated runtime context for this context. + void* GetRuntimeContext() const; // Get the IReferenceTracker instance without going through the reference proxy. - IReferenceTracker* GetReferenceTrackerFast(); + IReferenceTracker* GetReferenceTrackerFast() const; // Get a type instance of the desired type through the reference proxy. template @@ -133,4 +153,89 @@ class NativeObjectWrapperInstance } }; +// API for creating an IAgileReference instance to a supplied IUnknown instance. +// See https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-rogetagilereference +// +// N.B. Using a template so that callers are required to provide an explicit IUnknown instance. +template +HRESULT CreateAgileReference( + _In_ T* object, + _Outptr_ IAgileReference** agileReference); + +// Class used to hold COM objects (i.e. IUnknown base class) +// This class mimics the semantics of ATL::CComPtr (https://docs.microsoft.com/cpp/atl/reference/ccomptr-class). +template +struct ComHolder +{ + T* p; + + ComHolder() + : p{ nullptr } + { } + + ComHolder(_In_ const ComHolder&) = delete; + ComHolder& operator=(_In_ const ComHolder&) = delete; + + ComHolder(_In_ ComHolder&& other) + : p{ other.Detach() } + { } + + ComHolder& operator=(_In_ ComHolder&& other) + { + Release(); + p = other.Detach(); + return (*this); + } + + ComHolder(_In_ T* i) + : p{ i } + { + _ASSERTE(p != nullptr); + (void)p->AddRef(); + } + + ~ComHolder() + { + Release(); + } + + T** operator&() + { + return &p; + } + + T* operator->() + { + return p; + } + + operator T*() + { + return p; + } + + void Attach(_In_opt_ T* i) noexcept + { + Release(); + if (i != nullptr) + (void)i->AddRef(); + p = i; + } + + T* Detach() noexcept + { + T* tmp = p; + p = nullptr; + return tmp; + } + + void Release() noexcept + { + if (p != nullptr) + { + (void)p->Release(); + p = nullptr; + } + } +}; #endif // _INTEROP_COMWRAPPERS_H_ \ No newline at end of file diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 3b3dab15fd3d8d..8c2edde6c2d0ae 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -5,6 +5,12 @@ #ifndef _INTEROP_INC_INTEROPLIB_H_ #define _INTEROP_INC_INTEROPLIB_H_ +namespace InteropLibImports +{ + // Forward declaration of External Object Context iterator. + class ExtObjCxtIterator; +} + namespace InteropLib { using OBJECTHANDLE = void*; @@ -13,19 +19,30 @@ namespace InteropLib namespace Com { - using NativeObjectWrapperHandle = void*; - // Create an IUnknown instance that represents the supplied managed object instance. HRESULT CreateWrapperForObject( _In_ OBJECTHANDLE instance, _In_ INT32 vtableCount, _In_ void* vtables, _In_ INT32 flags, - _Outptr_ IUnknown** comObject) noexcept; + _Outptr_ IUnknown** wrapper) noexcept; - // Destroy the supplied COM wrapper + // Destroy the supplied wrapper void DestroyWrapperForObject(_In_ void* wrapper) noexcept; + // Allocate a wrapper context for an external object. + // The runtime supplies the external object, flags, and a memory + // request in order to bring the object into the runtime. + // The returned context memory is guaranteed to be initialized to zero. + HRESULT CreateWrapperForExternal( + _In_ IUnknown* external, + _In_ INT32 flags, + _In_ size_t contextSize, + _Outptr_ void** context) noexcept; + + // Destroy the supplied wrapper. + void DestroyWrapperForExternal(_In_ void* context) noexcept; + // Register the default callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept; diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index f58317a0c8cc0d..5139a84fbf7a63 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -12,16 +12,22 @@ namespace InteropLibImports enum class AllocScenario { ManagedObjectWrapper, + NativeObjectWrapper, }; // Allocate the given amount of memory. - void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario); + void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) noexcept; // Free the previously allocated memory. - void MemFree(_In_ void* mem, _In_ AllocScenario scenario); + void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept; // Delete Object instance handle - void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle); + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; + + // Get next External Object Context from the iterator. + // S_OK - Context is valid. + // S_FALSE - Iterator has reached end and context out parameter is set to NULL. + HRESULT IteratorNext(_In_ ExtObjCxtIterator* iter, _Outptr_result_maybenull_ void** context) noexcept; // Given a ComWrappers implementation, get or create // an IReferenceTrackerTarget instance for the supplied diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index 0795e79907cec3..e971a767c4ec82 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -5,22 +5,27 @@ #ifndef _INTEROP_PLATFORM_H_ #define _INTEROP_PLATFORM_H_ +#include #include #include +#ifndef _ASSERTE +#define _ASSERTE(x) assert((x)) +#endif + #ifdef _WIN32 #include + +// Common macro for working in COM +#define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return hr; } } +#define RETURN_VOID_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return; } } + #endif // _WIN32 #define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") // BEGIN [TODO] Remove -#include #include - -#ifndef _ASSERTE -#define _ASSERTE(x) assert((x)) -#endif // END [TODO] Remove #endif // _INTEROP_PLATFORM_H_ \ No newline at end of file diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 30d751cd9b06e9..9420a84e02b90d 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -26,6 +26,7 @@ namespace { const HandleType InstanceHandleType{ HNDTYPE_STRONG }; + const HandleType ComWrappersImplHandleType{ HNDTYPE_STRONG }; void* CallComputeVTables( _In_ OBJECTREF impl, @@ -36,6 +37,7 @@ namespace CONTRACTL { THROWS; + GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(impl != NULL); PRECONDITION(instance != NULL); @@ -77,6 +79,7 @@ namespace CONTRACTL { THROWS; + GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(impl != NULL); PRECONDITION(externalComObject != NULL); @@ -105,11 +108,204 @@ namespace return retObjRef; } + + // This class is used to track the external object within the runtime. + struct ExternalObjectContext + { + void* Identity; + DWORD SyncBlockIndex; + }; + + class ExtObjCxtCache + { + static ExtObjCxtCache* g_Instance; + + public: // static + static ExtObjCxtCache* GetInstance() + { + // [TODO] Properly allocate the cache + if (g_Instance == nullptr) + g_Instance = new ExtObjCxtCache(); + + return g_Instance; + } + + public: // Inner class definitions + class Traits : public DefaultSHashTraits + { + public: + using key_t = void*; + static const key_t GetKey(_In_ element_t e) { LIMITED_METHOD_CONTRACT; return (key_t)e->Identity; } + static count_t Hash(_In_ key_t key) { LIMITED_METHOD_CONTRACT; return (count_t)key; } + static bool Equals(_In_ key_t lhs, _In_ key_t rhs) { LIMITED_METHOD_CONTRACT; return (lhs == rhs); } + }; + + // Alias some useful types + using Element = SHash::element_t; + using Iterator = SHash::Iterator; + + class LockHolder : public CrstHolder + { + public: + LockHolder(_In_ ExtObjCxtCache *cache) + : CrstHolder(&cache->_lock) + { + // This cache must be locked in Cooperative mode + // since releases of wrappers can occur during a GC. + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + } + }; + + private: + friend class InteropLibImports::ExtObjCxtIterator; + SHash _hashMap; + Crst _lock; + + ExtObjCxtCache() + : _lock(CrstExternalObjectContextCache, CRST_UNSAFE_COOPGC) + { } + ~ExtObjCxtCache() = default; + + public: + bool IsLockHeld() + { + WRAPPER_NO_CONTRACT; + return (_lock.OwnedByCurrentThread() != FALSE); + } + + ExternalObjectContext* Find(_In_ IUnknown* instance) + { + CONTRACT(ExternalObjectContext*) + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(IsLockHeld()); + PRECONDITION(instance != NULL); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // Forbid the GC from messing with the hash table. + GCX_FORBID(); + + RETURN _hashMap.Lookup(instance); + } + + ExternalObjectContext* Add(_In_ ExternalObjectContext* cxt) + { + CONTRACT(ExternalObjectContext*) + { + THROWS; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(IsLockHeld()); + PRECONDITION(!Traits::IsNull(cxt)); + PRECONDITION(!Traits::IsDeleted(cxt)); + PRECONDITION(cxt->Identity != NULL); + PRECONDITION(Find(static_cast(cxt->Identity)) == NULL); + POSTCONDITION(RETVAL == cxt); + } + CONTRACT_END; + + _hashMap.Add(cxt); + RETURN cxt; + } + + ExternalObjectContext* FindOrAdd(_In_ IUnknown* key, _In_ ExternalObjectContext* newCxt) + { + CONTRACT(ExternalObjectContext*) + { + THROWS; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(IsLockHeld()); + PRECONDITION(key != NULL); + PRECONDITION(!Traits::IsNull(newCxt)); + PRECONDITION(!Traits::IsDeleted(newCxt)); + PRECONDITION(key == newCxt->Identity); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + // Forbid the GC from messing with the hash table. + GCX_FORBID(); + + ExternalObjectContext* cxt = Find(key); + if (Traits::IsNull(cxt)) + cxt = Add(newCxt); + + RETURN cxt; + } + + void Remove(_In_ ExternalObjectContext* cxt) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(!Traits::IsNull(cxt)); + PRECONDITION(!Traits::IsDeleted(cxt)); + PRECONDITION(cxt->Identity != NULL); + + // The GC thread doesn't have to take the lock + // since all other threads access in cooperative mode + PRECONDITION( + (IsLockHeld() && GetThread()->PreemptiveGCDisabled()) + || Debug_IsLockedViaThreadSuspension()); + } + CONTRACTL_END; + + _hashMap.Remove(cxt->Identity); + } + }; + + // Global instance + ExtObjCxtCache* ExtObjCxtCache::g_Instance; + + // Wrapper for External Object Contexts + struct ExtObjCxtHolder + { + void* _cxt; + ExtObjCxtHolder() + : _cxt(nullptr) + { } + ~ExtObjCxtHolder() + { + if (_cxt != nullptr) + InteropLib::Com::DestroyWrapperForExternal(_cxt); + } + ExternalObjectContext* operator->() + { + return (ExternalObjectContext*)_cxt; + } + void** operator&() + { + return &_cxt; + } + operator ExternalObjectContext*() + { + return (ExternalObjectContext*)_cxt; + } + void* Detach() + { + void* t = _cxt; + _cxt = nullptr; + return t; + } + }; } namespace InteropLibImports { - void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) + void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) noexcept { CONTRACTL { @@ -122,7 +318,7 @@ namespace InteropLibImports return ::malloc(sizeInBytes); } - void MemFree(_In_ void* mem, _In_ AllocScenario scenario) + void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept { CONTRACTL { @@ -135,7 +331,7 @@ namespace InteropLibImports ::free(mem); } - void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept { CONTRACTL { @@ -195,6 +391,44 @@ namespace InteropLibImports return S_OK; } + + class ExtObjCxtIterator + { + public: + ExtObjCxtIterator(_In_ ExtObjCxtCache* cache) + : Curr{ cache->_hashMap.Begin() } + , End{ cache->_hashMap.End() } + { } + + ExtObjCxtCache::Iterator Curr; + ExtObjCxtCache::Iterator End; + }; + + HRESULT IteratorNext(_In_ ExtObjCxtIterator* iter, _Outptr_result_maybenull_ void** context) noexcept + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(iter != NULL); + PRECONDITION(context != NULL); + + // Should only be called during a GC suspension + PRECONDITION(Debug_IsLockedViaThreadSuspension()); + } + CONTRACTL_END; + + if (iter->Curr == iter->End) + { + *context = NULL; + return S_FALSE; + } + + ExtObjCxtCache::Element e = *iter->Curr++; + *context = e; + return S_OK; + } } #ifdef FEATURE_COMINTEROP @@ -271,7 +505,9 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( // If the managed object wrapper couldn't be set, then // it should be possible to get the current one. if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { UNREACHABLE(); + } } } } @@ -320,6 +556,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( BEGIN_QCALL; HRESULT hr; + ExternalObjectContext* extObjCxt; IUnknown* externalComObject = reinterpret_cast(ext); // Determine the true identity of the object @@ -327,26 +564,67 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( hr = externalComObject->QueryInterface(IID_IUnknown, &identity); _ASSERTE(hr == S_OK); - // Switch to COOP mode in order to check if the the InteropLib already - // has an object for the external COM object or to create a new one. + // Switch to COOP mode in order to check if the external object is already + // known or a new one should be created. { GCX_COOP(); struct { OBJECTREF implRef; - OBJECTREF newObjRef; + OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); - _ASSERTE(gc.implRef != NULL); + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + + { + // Query the external object cache + ExtObjCxtCache::LockHolder lock(cache); + extObjCxt = cache->Find(identity); + } + + if (extObjCxt != NULL) + { + if (extObjCxt->SyncBlockIndex == 0) + { + // [TODO] We are in a bad spot? + } + + gc.objRef = ObjectToOBJECTREF(g_pSyncTable[extObjCxt->SyncBlockIndex].m_Object); + } + else + { + ExtObjCxtHolder newContext; + hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), &newContext); + if (FAILED(hr)) + COMPlusThrow(hr); + + gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); + _ASSERTE(gc.implRef != NULL); + + // Call the implementation to create an external object wrapper. + gc.objRef = CallGetObject(gc.implRef, identity, flags); + if (gc.objRef == NULL) + COMPlusThrow(kArgumentNullException); + + newContext->Identity = (void*)identity; + newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); + + { + ExtObjCxtCache::LockHolder lock(cache); + extObjCxt = cache->FindOrAdd(identity, newContext); + } - gc.newObjRef = CallGetObject(gc.implRef, identity, flags); + // Detach from the holder if the returned context matches the new context + // since it means the new context was inserted. + if (extObjCxt == newContext) + (void)newContext.Detach(); + } // Set the return value - retValue.Set(gc.newObjRef); + retValue.Set(gc.objRef); GCPROTECT_END(); } @@ -359,7 +637,6 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( { QCALL_CONTRACT; - const HandleType implHandleType{ HNDTYPE_STRONG }; OBJECTHANDLE implHandle; BEGIN_QCALL; @@ -375,11 +652,11 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); _ASSERTE(implRef != NULL); - implHandle = GetAppDomain()->CreateTypedHandle(implRef, implHandleType); + implHandle = GetAppDomain()->CreateTypedHandle(implRef, ComWrappersImplHandleType); if (!InteropLib::Com::RegisterReferenceTrackerHostCallback(implHandle)) { - DestroyHandleCommon(implHandle, implHandleType); + DestroyHandleCommon(implHandle, ComWrappersImplHandleType); COMPlusThrow(kInvalidOperationException, IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS); } @@ -412,6 +689,7 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) CONTRACTL { NOTHROW; + GC_NOTRIGGER; MODE_ANY; PRECONDITION(wrapper != NULL); } From 33720f7b5028de55890ffc65f2efba191ea49640 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 30 Jan 2020 13:29:53 -0800 Subject: [PATCH 17/83] Remove unnecessary GCProtect sections. Allocate External Object Context cache. --- .../src/interop/inc/interoplibimports.h | 2 +- src/coreclr/src/vm/interoplibinterface.cpp | 578 +++++++++--------- 2 files changed, 307 insertions(+), 273 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 5139a84fbf7a63..68b489fadfe144 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -37,7 +37,7 @@ namespace InteropLibImports _In_ IUnknown* externalComObject, _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, - _Outptr_ IUnknown** trackerTarget) noexcept; + _Outptr_ void** trackerTarget) noexcept; } #endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 9420a84e02b90d..f4615d1833fd2f 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -25,107 +25,60 @@ namespace { - const HandleType InstanceHandleType{ HNDTYPE_STRONG }; - const HandleType ComWrappersImplHandleType{ HNDTYPE_STRONG }; + // This class is used to track the external object within the runtime. + struct ExternalObjectContext + { + void* Identity; + DWORD SyncBlockIndex; + }; - void* CallComputeVTables( - _In_ OBJECTREF impl, - _In_ OBJECTREF instance, - _In_ INT32 flags, - _Out_ DWORD* vtableCount) + // Holder for a External Object Context + struct ExtObjCxtHolder { - CONTRACTL + ExternalObjectContext* _cxt; + ExtObjCxtHolder() + : _cxt(nullptr) + { } + ~ExtObjCxtHolder() { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(impl != NULL); - PRECONDITION(instance != NULL); - PRECONDITION(vtableCount != NULL); + if (_cxt != nullptr) + InteropLib::Com::DestroyWrapperForExternal(_cxt); } - CONTRACTL_END; - - void* vtables = NULL; - - struct + ExternalObjectContext* operator->() { - OBJECTREF implRef; - OBJECTREF instRef; - } gc; - ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); - - gc.implRef = impl; - gc.instRef = instance; - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); - DECLARE_ARGHOLDER_ARRAY(args, 4); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.implRef); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.instRef); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(vtableCount); - CALL_MANAGED_METHOD(vtables, void*, args); - - GCPROTECT_END(); - - return vtables; - } - - OBJECTREF CallGetObject( - _In_ OBJECTREF impl, - _In_ IUnknown* externalComObject, - _In_ INT32 flags) - { - CONTRACTL + return (ExternalObjectContext*)_cxt; + } + ExternalObjectContext** operator&() { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(impl != NULL); - PRECONDITION(externalComObject != NULL); + return &_cxt; } - CONTRACTL_END; - - OBJECTREF retObjRef; - - struct + operator ExternalObjectContext*() { - OBJECTREF implRef; - } gc; - ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); - - gc.implRef = impl; - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 3); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.implRef); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); - CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); - - GCPROTECT_END(); - - return retObjRef; - } - - // This class is used to track the external object within the runtime. - struct ExternalObjectContext - { - void* Identity; - DWORD SyncBlockIndex; + return _cxt; + } + ExternalObjectContext* Detach() + { + ExternalObjectContext* t = _cxt; + _cxt = nullptr; + return t; + } }; class ExtObjCxtCache { - static ExtObjCxtCache* g_Instance; + static Volatile g_Instance; public: // static static ExtObjCxtCache* GetInstance() { - // [TODO] Properly allocate the cache - if (g_Instance == nullptr) - g_Instance = new ExtObjCxtCache(); + if (g_Instance.Load() == NULL) + { + ExtObjCxtCache* instMaybe = new ExtObjCxtCache(); + + // Attempt to set the global instance. + if (NULL != FastInterlockCompareExchangePointer(&g_Instance, instMaybe, NULL)) + delete instMaybe; + } return g_Instance; } @@ -206,8 +159,7 @@ namespace GC_NOTRIGGER; MODE_COOPERATIVE; PRECONDITION(IsLockHeld()); - PRECONDITION(!Traits::IsNull(cxt)); - PRECONDITION(!Traits::IsDeleted(cxt)); + PRECONDITION(!Traits::IsNull(cxt) && !Traits::IsDeleted(cxt)); PRECONDITION(cxt->Identity != NULL); PRECONDITION(Find(static_cast(cxt->Identity)) == NULL); POSTCONDITION(RETVAL == cxt); @@ -227,8 +179,7 @@ namespace MODE_COOPERATIVE; PRECONDITION(IsLockHeld()); PRECONDITION(key != NULL); - PRECONDITION(!Traits::IsNull(newCxt)); - PRECONDITION(!Traits::IsDeleted(newCxt)); + PRECONDITION(!Traits::IsNull(newCxt) && !Traits::IsDeleted(newCxt)); PRECONDITION(key == newCxt->Identity); POSTCONDITION(CheckPointer(RETVAL)); } @@ -251,8 +202,7 @@ namespace NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(!Traits::IsNull(cxt)); - PRECONDITION(!Traits::IsDeleted(cxt)); + PRECONDITION(!Traits::IsNull(cxt) && !Traits::IsDeleted(cxt)); PRECONDITION(cxt->Identity != NULL); // The GC thread doesn't have to take the lock @@ -268,39 +218,249 @@ namespace }; // Global instance - ExtObjCxtCache* ExtObjCxtCache::g_Instance; + Volatile ExtObjCxtCache::g_Instance; - // Wrapper for External Object Contexts - struct ExtObjCxtHolder + // Defined handle types for the specific object uses. + const HandleType InstanceHandleType{ HNDTYPE_STRONG }; + const HandleType ComWrappersImplHandleType{ HNDTYPE_STRONG }; + + void* CallComputeVTables( + _In_ OBJECTREF* implPROTECTED, + _In_ OBJECTREF* instancePROTECTED, + _In_ INT32 flags, + _Out_ DWORD* vtableCount) { - void* _cxt; - ExtObjCxtHolder() - : _cxt(nullptr) - { } - ~ExtObjCxtHolder() + CONTRACTL { - if (_cxt != nullptr) - InteropLib::Com::DestroyWrapperForExternal(_cxt); + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(implPROTECTED != NULL); + PRECONDITION(instancePROTECTED != NULL); + PRECONDITION(vtableCount != NULL); } - ExternalObjectContext* operator->() + CONTRACTL_END; + + void* vtables = NULL; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); + DECLARE_ARGHOLDER_ARRAY(args, 4); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(vtableCount); + CALL_MANAGED_METHOD(vtables, void*, args); + + return vtables; + } + + OBJECTREF CallGetObject( + _In_ OBJECTREF* implPROTECTED, + _In_ IUnknown* externalComObject, + _In_ INT32 flags) + { + CONTRACTL { - return (ExternalObjectContext*)_cxt; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(implPROTECTED != NULL); + PRECONDITION(externalComObject != NULL); } - void** operator&() + CONTRACTL_END; + + OBJECTREF retObjRef; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); + DECLARE_ARGHOLDER_ARRAY(args, 3); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); + + return retObjRef; + } + + void* GetOrCreateComInterfaceForObjectInternal( + _In_ OBJECTREF impl, + _In_ OBJECTREF instance, + _In_ INT32 flags) + { + CONTRACT(void*) { - return &_cxt; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(impl != NULL); + PRECONDITION(instance != NULL); + POSTCONDITION(CheckPointer(RETVAL)); } - operator ExternalObjectContext*() + CONTRACT_END; + + HRESULT hr; + + SafeComHolder newWrapper; + void* wrapper = NULL; + + struct { - return (ExternalObjectContext*)_cxt; + OBJECTREF implRef; + OBJECTREF instRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = impl; + gc.instRef = instance; + + // Check the object's SyncBlock for a managed object wrapper. + SyncBlock* syncBlock = gc.instRef->GetSyncBlock(); + InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); + + // Query the associated InteropSyncBlockInfo for an existing managed object wrapper. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { + // Compute VTables for the new existing COM object using the supplied COM Wrappers implementation. + // + // N.B. Calling to compute the associated VTables is perhaps early since no lock + // is taken. However, a key assumption here is that the returned memory will be + // idempotent for the same object. + DWORD vtableCount; + void* vtables = CallComputeVTables(&gc.implRef, &gc.instRef, flags, &vtableCount); + + // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { + OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); + + // Call the InteropLib and create the associated managed object wrapper. + hr = InteropLib::Com::CreateWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); + if (FAILED(hr)) + { + DestroyHandleCommon(instHandle, InstanceHandleType); + COMPlusThrowHR(hr); + } + _ASSERTE(!newWrapper.IsNull()); + + // Try setting the newly created managed object wrapper on the InteropSyncBlockInfo. + if (!interopInfo->TrySetManagedObjectComWrapper(newWrapper)) + { + // The new wrapper couldn't be set which means a wrapper already exists. + newWrapper.Release(); + + // If the managed object wrapper couldn't be set, then + // it should be possible to get the current one. + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + { + UNREACHABLE(); + } + } + } } - void* Detach() + + // Determine what to return. + if (!newWrapper.IsNull()) { - void* t = _cxt; - _cxt = nullptr; - return t; + // A new managed object wrapper was created, remove the object from the holder. + // No AddRef() here since the wrapper should be created with a reference. + wrapper = newWrapper.Extract(); } - }; + else + { + _ASSERTE(wrapper != NULL); + // It is possible the supplied wrapper is no longer valid. If so, reactivate the + // wrapper with the object instance's new handle. If this reactivation + // wasn't needed, delete the handle. + OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); + hr = InteropLib::Com::EnsureActiveWrapperAndAddRef(static_cast(wrapper), instHandle); + if (hr != S_OK) + DestroyHandleCommon(instHandle, InstanceHandleType); + + if (FAILED(hr)) + COMPlusThrowHR(hr); + } + + GCPROTECT_END(); + + RETURN wrapper; + } + + OBJECTREF GetOrCreateObjectForComInstanceInternal( + _In_ OBJECTREF impl, + _In_ IUnknown* identity, + _In_ INT32 flags) + { + CONTRACT(OBJECTREF) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(impl != NULL); + PRECONDITION(identity != NULL); + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + + HRESULT hr; + ExternalObjectContext* extObjCxt; + + struct + { + OBJECTREF implRef; + OBJECTREF objRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.implRef = impl; + + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + + { + // Query the external object cache + ExtObjCxtCache::LockHolder lock(cache); + extObjCxt = cache->Find(identity); + } + + if (extObjCxt != NULL) + { + if (extObjCxt->SyncBlockIndex == 0) + { + // [TODO] We are in a bad spot? + } + + gc.objRef = ObjectToOBJECTREF(g_pSyncTable[extObjCxt->SyncBlockIndex].m_Object); + } + else + { + ExtObjCxtHolder newContext; + hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), (void**)&newContext); + if (FAILED(hr)) + COMPlusThrow(hr); + + // Call the implementation to create an external object wrapper. + gc.objRef = CallGetObject(&gc.implRef, identity, flags); + if (gc.objRef == NULL) + COMPlusThrow(kArgumentNullException); + + newContext->Identity = (void*)identity; + newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); + + { + ExtObjCxtCache::LockHolder lock(cache); + extObjCxt = cache->FindOrAdd(identity, newContext); + } + + // Detach from the holder if the returned context matches the new context + // since it means the new context was inserted. + if (extObjCxt == newContext) + (void)newContext.Detach(); + } + + GCPROTECT_END(); + + RETURN gc.objRef; + } } namespace InteropLibImports @@ -350,7 +510,7 @@ namespace InteropLibImports _In_ IUnknown* externalComObject, _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, - _Outptr_ IUnknown** trackerTarget) noexcept + _Outptr_ void** trackerTarget) noexcept { CONTRACTL { @@ -362,34 +522,44 @@ namespace InteropLibImports } CONTRACTL_END; + HRESULT hr = S_OK; ::OBJECTHANDLE implHandle = static_cast<::OBJECTHANDLE>(impl); + *trackerTarget = NULL; + { GCX_COOP(); struct { OBJECTREF implRef; - OBJECTREF newObjRef; + OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = ObjectFromHandle(implHandle); - _ASSERTE(gc.implRef != NULL); - // - // Get wrapper for external object - // - - // - // Get wrapper for managed object - // + EX_TRY + { + // Get wrapper for external object + gc.objRef = GetOrCreateObjectForComInstanceInternal( + gc.implRef, + externalComObject, + externalObjectFlags); + + // Get wrapper for managed object + *trackerTarget = GetOrCreateComInterfaceForObjectInternal( + gc.implRef, + gc.objRef, + trackerTargetFlags); + } + EX_CATCH_HRESULT(hr); GCPROTECT_END(); } - return S_OK; + return hr; } class ExtObjCxtIterator @@ -440,101 +610,18 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( { QCALL_CONTRACT; - HRESULT hr; - - SafeComHolder newWrapper; void* wrapper = NULL; BEGIN_QCALL; - // Switch to COOP mode to check if the object already - // has a wrapper in its syncblock. + // Switch to Cooperative mode since object references + // are being manipulated. { GCX_COOP(); - - struct - { - OBJECTREF implRef; - OBJECTREF instRef; - } gc; - ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); - - gc.instRef = ObjectToOBJECTREF(*instance.m_ppObject); - _ASSERTE(gc.instRef != NULL); - - // Check the object's SyncBlock for a managed object wrapper. - SyncBlock* syncBlock = gc.instRef->GetSyncBlock(); - InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); - - // Query the associated InteropSyncBlockInfo for an existing managed object wrapper. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) - { - // Get the supplied COM Wrappers implementation to request VTable computation. - gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); - _ASSERTE(gc.implRef != NULL); - - // Compute VTables for the new existing COM object - // - // N.B. Calling to compute the associated VTables is perhaps early since no lock - // is taken. However, a key assumption here is that the returned memory will be - // idempotent for the same object. - DWORD vtableCount; - void* vtables = CallComputeVTables(gc.implRef, gc.instRef, flags, &vtableCount); - - // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) - { - OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); - - // Call the InteropLib and create the associated managed object wrapper. - hr = InteropLib::Com::CreateWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); - if (FAILED(hr)) - { - DestroyHandleCommon(instHandle, InstanceHandleType); - COMPlusThrowHR(hr); - } - _ASSERTE(!newWrapper.IsNull()); - - // Try setting the newly created managed object wrapper on the InteropSyncBlockInfo. - if (!interopInfo->TrySetManagedObjectComWrapper(newWrapper)) - { - // The new wrapper couldn't be set which means a wrapper already exists. - newWrapper.Release(); - - // If the managed object wrapper couldn't be set, then - // it should be possible to get the current one. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) - { - UNREACHABLE(); - } - } - } - } - - // Determine what to return. - if (!newWrapper.IsNull()) - { - // A new managed object wrapper was created, remove the object from the holder. - // No AddRef() here since the wrapper should be created with a reference. - wrapper = newWrapper.Extract(); - } - else - { - _ASSERTE(wrapper != NULL); - // It is possible the supplied wrapper is no longer valid. If so, reactivate the - // wrapper with the object instance's new handle. If this reactivation - // wasn't needed, delete the handle. - OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); - hr = InteropLib::Com::EnsureActiveWrapperAndAddRef(static_cast(wrapper), instHandle); - if (hr != S_OK) - DestroyHandleCommon(instHandle, InstanceHandleType); - - if (FAILED(hr)) - COMPlusThrowHR(hr); - } - - GCPROTECT_END(); + wrapper = GetOrCreateComInterfaceForObjectInternal( + ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), + ObjectToOBJECTREF(*instance.m_ppObject), + flags); } END_QCALL; @@ -556,7 +643,6 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( BEGIN_QCALL; HRESULT hr; - ExternalObjectContext* extObjCxt; IUnknown* externalComObject = reinterpret_cast(ext); // Determine the true identity of the object @@ -564,69 +650,17 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( hr = externalComObject->QueryInterface(IID_IUnknown, &identity); _ASSERTE(hr == S_OK); - // Switch to COOP mode in order to check if the external object is already - // known or a new one should be created. + // Switch to Cooperative mode since object references + // are being manipulated. { GCX_COOP(); - - struct - { - OBJECTREF implRef; - OBJECTREF objRef; - } gc; - ::ZeroMemory(&gc, sizeof(gc)); - GCPROTECT_BEGIN(gc); - - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - - { - // Query the external object cache - ExtObjCxtCache::LockHolder lock(cache); - extObjCxt = cache->Find(identity); - } - - if (extObjCxt != NULL) - { - if (extObjCxt->SyncBlockIndex == 0) - { - // [TODO] We are in a bad spot? - } - - gc.objRef = ObjectToOBJECTREF(g_pSyncTable[extObjCxt->SyncBlockIndex].m_Object); - } - else - { - ExtObjCxtHolder newContext; - hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), &newContext); - if (FAILED(hr)) - COMPlusThrow(hr); - - gc.implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); - _ASSERTE(gc.implRef != NULL); - - // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(gc.implRef, identity, flags); - if (gc.objRef == NULL) - COMPlusThrow(kArgumentNullException); - - newContext->Identity = (void*)identity; - newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); - - { - ExtObjCxtCache::LockHolder lock(cache); - extObjCxt = cache->FindOrAdd(identity, newContext); - } - - // Detach from the holder if the returned context matches the new context - // since it means the new context was inserted. - if (extObjCxt == newContext) - (void)newContext.Detach(); - } + OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal( + ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), + identity, + flags); // Set the return value - retValue.Set(gc.objRef); - - GCPROTECT_END(); + retValue.Set(newObj); } END_QCALL; From 1a87a117947830f1bb9879953293cbaa1b771124 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 30 Jan 2020 19:26:06 -0800 Subject: [PATCH 18/83] Retain the External Object Context in the SyncBlock and handle cleanup. --- src/coreclr/src/interop/agilereferences.cpp | 2 +- src/coreclr/src/interop/comwrappers.cpp | 54 ++++++------- src/coreclr/src/interop/comwrappers.h | 4 +- src/coreclr/src/vm/interoplibinterface.cpp | 87 +++++++++++++++++++-- src/coreclr/src/vm/interoplibinterface.h | 5 +- src/coreclr/src/vm/interoputil.cpp | 12 +++ src/coreclr/src/vm/syncblk.h | 24 +++++- 7 files changed, 147 insertions(+), 41 deletions(-) diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp index 92e454a2e1cad4..9dbea37ed854bf 100644 --- a/src/coreclr/src/interop/agilereferences.cpp +++ b/src/coreclr/src/interop/agilereferences.cpp @@ -29,7 +29,7 @@ HRESULT CreateAgileReference( _In_ IUnknown* object, _Outptr_ IAgileReference** agileReference) { - // [TODO] Handle this on pre-Windows 8.1 plaforms + // [TODO] Fail gracefully on pre-Windows 8.1 plaforms. _ASSERTE(object != nullptr && agileReference != nullptr); return ::RoGetAgileReference(AGILEREFERENCE_DEFAULT, __uuidof(object), object, agileReference); } diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index aaa14d027df121..2db3a8e20d8fff 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -12,14 +12,14 @@ namespace ABI //--------------------------------------------------------------------------------- // Dispatch section of the ManagedObjectWrapper (MOW) // - // Within the dispatch section, the ManagedObjectWrapper itself is inserted at all 16 byte + // Within the dispatch section, the ManagedObjectWrapper itself is inserted at a defined // aligned location. This allows the simple masking of the any ComInterfaceDispatch* to get - // access to the ManagedObjectWrapper by masking the lower 4 bits. Below is a sketch of how - // the dispatch section would appear in a 32-bit process. + // access to the ManagedObjectWrapper by masking the lower N bits. Below is a sketch of how + // the dispatch section would appear in a 32-bit process for a 16 bit alignment. // // 16 byte aligned Vtable // +-----------+ - // | MOW this | + // | MOW this | // +-----------+ +-----+ // COM IP-->| VTable ptr|----------------------------->|slot1| // +-----------+ +-----+ +-----+ @@ -27,13 +27,13 @@ namespace ABI // +-----------+ +-----+ + + // | VTable ptr| | ....| | ... | // +-----------+ + + + + - // | MOW this | |slotN| |slotN| + // | MOW this | |slotN| |slotN| // + + +-----+ +-----+ // | .... | // +-----------+ // // A 16 byte alignment permits a ratio of 3:1 COM vtables to ManagedObjectWrapper 'this' - // pointers in 32-bit process, but in 64-bit process the mapping is unfortunately only 1:1. + // pointers in a 32-bit process, but in a 64-bit process the mapping is only 1:1. // See the dispatch section building API below for an example of how indexing works. //-------------------------------------------------------------------------------- @@ -56,7 +56,7 @@ namespace ABI return (reinterpret_cast(disp) & DispatchThisPtrMask) != 0; } - // Given the number of dispatch entries compute the needed number of 'this' pointer entries. + // Given the number of dispatch entries, compute the needed number of 'this' pointer entries. constexpr size_t ComputeThisPtrForDispatchSection(_In_ size_t dispatchCount) { return (dispatchCount / ABI::EntriesPerThisPtr) + ((dispatchCount % ABI::EntriesPerThisPtr) == 0 ? 0 : 1); @@ -68,18 +68,18 @@ namespace ABI { _ASSERTE(section != nullptr); - // If the dispatch section is not properly aligned by default, we use - // utilize the padding to make sure the dispatch section can be aligned. + // If the dispatch section is not properly aligned by default, we + // utilize the padding to make sure the dispatch section is aligned. while ((reinterpret_cast(section) % ABI::DispatchAlignmentThisPtr) != 0) { - // Check if there is padding to attempt an alignment + // Check if there is padding to attempt an alignment. if (extraPadding <= 0) return nullptr; extraPadding -= sizeof(void*); #ifdef _DEBUG - // Poison unused sections of the section + // Poison unused portions of the section. std::memset(section, 0xff, sizeof(void*)); #endif @@ -102,13 +102,13 @@ namespace ABI _In_ size_t entrySetCount, _In_ const EntrySet* entrySets) { - // Define dispatch section iterator + // Define dispatch section iterator. const void** currDisp = reinterpret_cast(dispatchSection); - // Keep rolling count of dispatch entries + // Keep rolling count of dispatch entries. int32_t dispCount = 0; - // Iterate over all interface entry sets + // Iterate over all interface entry sets. const EntrySet* curr = entrySets; const EntrySet* end = entrySets + entrySetCount; for (; curr != end; ++curr) @@ -116,7 +116,7 @@ namespace ABI const ComInterfaceEntry* currEntry = curr->start; int32_t entryCount = curr->count; - // Update dispatch section with 'this' pointer and vtables + // Update dispatch section with 'this' pointer and vtables. for (int32_t i = 0; i < entryCount; ++i, ++dispCount, ++currEntry) { // Insert the 'this' pointer at the appropriate locations @@ -143,7 +143,7 @@ namespace ABI return reinterpret_cast(dispatchSection); } - // Given the entry index, compute the dispatch index + // Given the entry index, compute the dispatch index. ComInterfaceDispatch* IndexIntoDispatchSection(_In_ int32_t i, _In_ ComInterfaceDispatch* dispatches) { // Convert the supplied zero based index into what it represents as a count. @@ -192,7 +192,7 @@ namespace return wrapper->Release(); } - // Hard-coded ManagedObjectWrapper IUnknown vtable + // Hard-coded ManagedObjectWrapper IUnknown vtable. const struct { decltype(&ManagedObjectWrapper_QueryInterface) QueryInterface; @@ -299,11 +299,11 @@ HRESULT ManagedObjectWrapper::Create( { _ASSERTE(objectHandle != nullptr && mow != nullptr); - // Maximum number of runtime supplied vtables + // Maximum number of runtime supplied vtables. ComInterfaceEntry runtimeDefinedLocal[4]; int32_t runtimeDefinedCount = 0; - // Check if the caller will provide the IUnknown table + // Check if the caller will provide the IUnknown table. if ((flags & CreateComInterfaceFlags::CallerDefinedIUnknown) == CreateComInterfaceFlags::None) { ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; @@ -311,7 +311,7 @@ HRESULT ManagedObjectWrapper::Create( curr.Vtable = &ManagedObjectWrapper_IUnknownImpl; } - // Check if the caller wants tracker support + // Check if the caller wants tracker support. if ((flags & CreateComInterfaceFlags::TrackerSupport) == CreateComInterfaceFlags::TrackerSupport) { ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; @@ -321,23 +321,23 @@ HRESULT ManagedObjectWrapper::Create( _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefinedLocal)); - // Compute size for ManagedObjectWrapper instance + // Compute size for ManagedObjectWrapper instance. const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ComInterfaceEntry); const size_t totalDefinedCount = static_cast(runtimeDefinedCount) + userDefinedCount; - // Compute the total entry size of dispatch section + // Compute the total entry size of dispatch section. const size_t totalDispatchSectionCount = ABI::ComputeThisPtrForDispatchSection(totalDefinedCount) + totalDefinedCount; const size_t totalDispatchSectionSize = totalDispatchSectionCount * sizeof(void*); - // Allocate memory for the ManagedObjectWrapper + // Allocate memory for the ManagedObjectWrapper. char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding, AllocScenario::ManagedObjectWrapper); if (wrapperMem == nullptr) return E_OUTOFMEMORY; - // Compute Runtime defined offset + // Compute Runtime defined offset. char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); - // Copy in runtime supplied COM interface entries + // Copy in runtime supplied COM interface entries. ComInterfaceEntry* runtimeDefined = nullptr; if (0 < runtimeDefinedCount) { @@ -345,7 +345,7 @@ HRESULT ManagedObjectWrapper::Create( runtimeDefined = reinterpret_cast(runtimeDefinedOffset); } - // Compute the dispatch section offset and ensure it is aligned + // Compute the dispatch section offset and ensure it is aligned. char* dispatchSectionOffset = runtimeDefinedOffset + totalRuntimeDefinedSize; dispatchSectionOffset = ABI::AlignDispatchSection(dispatchSectionOffset, ABI::AlignmentThisPtrMaxPadding); if (dispatchSectionOffset == nullptr) @@ -657,7 +657,6 @@ namespace InteropLib { ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); - // This should never happen. // A caller should not be destroying a wrapper without knowing if the wrapper is valid. _ASSERTE(wrapper != nullptr); @@ -692,7 +691,6 @@ namespace InteropLib { NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); - // This should never happen. // A caller should not be destroying a context without knowing if the context is valid. _ASSERTE(context != nullptr); diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 77daabd51863cf..03e416ccf8e1d7 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -176,11 +176,11 @@ struct ComHolder ComHolder(_In_ const ComHolder&) = delete; ComHolder& operator=(_In_ const ComHolder&) = delete; - ComHolder(_In_ ComHolder&& other) + ComHolder(_Inout_ ComHolder&& other) : p{ other.Detach() } { } - ComHolder& operator=(_In_ ComHolder&& other) + ComHolder& operator=(_Inout_ ComHolder&& other) { Release(); p = other.Detach(); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index f4615d1833fd2f..9dc5c27e489d17 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -28,10 +28,29 @@ namespace // This class is used to track the external object within the runtime. struct ExternalObjectContext { + static DWORD InvalidSyncBlockIndex = 0; + static DWORD CollectedFlag = ~0; + void* Identity; DWORD SyncBlockIndex; + DWORD IsCollected; + + void MarkCollected() + { + _ASSERTE(GCHeapUtilities::IsGCInProgress()); + SyncBlockIndex = InvalidSyncBlockIndex; + IsCollected = CollectedFlag; + } + + bool IsActive() const + { + return (IsCollected != CollectedFlag) + && (SyncBlockIndex != InvalidSyncBlockIndex); + } }; + static_assert((sizeof(ExternalObjectContext) % sizeof(void*)) == 0, "Keep context pointer size aligned"); + // Holder for a External Object Context struct ExtObjCxtHolder { @@ -189,7 +208,7 @@ namespace GCX_FORBID(); ExternalObjectContext* cxt = Find(key); - if (Traits::IsNull(cxt)) + if (cxt == NULL) cxt = Add(newCxt); RETURN cxt; @@ -316,6 +335,7 @@ namespace // Check the object's SyncBlock for a managed object wrapper. SyncBlock* syncBlock = gc.instRef->GetSyncBlock(); InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); + _ASSERTE(syncBlock->IsPrecious()); // Query the associated InteropSyncBlockInfo for an existing managed object wrapper. if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) @@ -424,7 +444,7 @@ namespace if (extObjCxt != NULL) { - if (extObjCxt->SyncBlockIndex == 0) + if (!extObjCxt->IsActive()) { // [TODO] We are in a bad spot? } @@ -433,6 +453,7 @@ namespace } else { + // Create context for the possibly new external COM object. ExtObjCxtHolder newContext; hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), (void**)&newContext); if (FAILED(hr)) @@ -443,18 +464,31 @@ namespace if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); + // Update the new context with the object details. newContext->Identity = (void*)identity; newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); + // Attempt to insert the new context into the cache. { ExtObjCxtCache::LockHolder lock(cache); extObjCxt = cache->FindOrAdd(identity, newContext); } - // Detach from the holder if the returned context matches the new context - // since it means the new context was inserted. + // If the returned context matches the new context it means the + // new context was inserted. if (extObjCxt == newContext) + { + // Update the object's SyncBlock with a handle to the context for runtime cleanup. + SyncBlock* syncBlock = gc.objRef->GetSyncBlock(); + InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); + _ASSERTE(syncBlock->IsPrecious()); + (void)interopInfo->TrySetExternalComObjectContext((void**)extObjCxt); + + // Detach from the holder to avoid cleanup. (void)newContext.Detach(); + } + + _ASSERTE(extObjCxt->IsActive()); } GCPROTECT_END(); @@ -483,7 +517,6 @@ namespace InteropLibImports CONTRACTL { NOTHROW; - MODE_ANY; PRECONDITION(mem != NULL); } CONTRACTL_END; @@ -496,7 +529,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; - MODE_PREEMPTIVE; + MODE_ANY; PRECONDITION(handle != NULL); } CONTRACTL_END; @@ -527,6 +560,8 @@ namespace InteropLibImports *trackerTarget = NULL; + // Switch to Cooperative mode since object references + // are being manipulated. { GCX_COOP(); @@ -723,7 +758,6 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) CONTRACTL { NOTHROW; - GC_NOTRIGGER; MODE_ANY; PRECONDITION(wrapper != NULL); } @@ -732,4 +766,43 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) InteropLib::Com::DestroyWrapperForObject(wrapper); } +void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* context) +{ + CONTRACTL + { + NOTHROW; + MODE_ANY; + PRECONDITION(context != NULL); + } + CONTRACTL_END; + +#ifdef _DEBUG + ExternalObjectContext* context = static_cast(contextRaw); + _ASSERTE(!context->IsActive()); +#endif + + InteropLib::Com::DestroyWrapperForExternal(context); +} + +void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* contextRaw) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(context != NULL); + PRECONDITION(GCHeapUtilities::IsGCInProgress()); + } + CONTRACTL_END; + + ExternalObjectContext* context = static_cast(contextRaw); + _ASSERTE(context->IsActive()); + context->MarkCollected(); + + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + _ASSERTE(cache != NULL); + cache->Remove(context); +} + #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 9eed2943d8a728..8747912f4bcc14 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -11,7 +11,7 @@ // Native calls for the managed ComWrappers API class ComWrappersNative { -public: +public: // Native QCalls for the abstract ComWrappers managed type. static void QCALLTYPE GetIUnknownImpl( _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, @@ -31,7 +31,10 @@ class ComWrappersNative static void QCALLTYPE RegisterForReferenceTrackerHost( _In_ QCall::ObjectHandleOnStack comWrappersImpl); +public: // Lifetime management for COM Wrappers static void DestroyManagedObjectComWrapper(_In_ void* wrapper); + static void DestroyExternalComObjectContext(_In_ void* context); + static void MarkExternalComObjectContextCollected(_In_ void* context); }; #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoputil.cpp b/src/coreclr/src/vm/interoputil.cpp index 1b2bb73fbf5bec..aef8966d39ffb7 100644 --- a/src/coreclr/src/vm/interoputil.cpp +++ b/src/coreclr/src/vm/interoputil.cpp @@ -1937,6 +1937,10 @@ void MinorCleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) RCW* pRCW = pInteropInfo->GetRawRCW(); if (pRCW) pRCW->MinorCleanup(); + + void* eoc; + if (pInteropInfo->TryGetExternalComObjectContext(&eoc)) + ComWrappersNative::MarkExternalComObjectContextCollected(eoc); } void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) @@ -1981,8 +1985,16 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) void* mocw; if (pInteropInfo->TryGetManagedObjectComWrapper(&mocw)) { + (void)pInteropInfo->TrySetManagedObjectComWrapper(NULL, mocw); ComWrappersNative::DestroyManagedObjectComWrapper(mocw); } + + void* eoc; + if (pInteropInfo->TryGetExternalComObjectContext(&eoc)) + { + (void)pInteropInfo->TrySetExternalComObjectContext(NULL, eoc); + ComWrappersNative::DestroyExternalComObjectContext(eoc); + } } void ReleaseRCWsInCachesNoThrow(LPVOID pCtxCookie) diff --git a/src/coreclr/src/vm/syncblk.h b/src/coreclr/src/vm/syncblk.h index 05e2b0ecadfd80..e0364df193b1b9 100644 --- a/src/coreclr/src/vm/syncblk.h +++ b/src/coreclr/src/vm/syncblk.h @@ -799,20 +799,40 @@ class InteropSyncBlockInfo } #ifndef DACCESS_COMPILE - bool TrySetManagedObjectComWrapper(_In_ void* mocw) + bool TrySetManagedObjectComWrapper(_In_ void* mocw, _In_ void* curr = NULL) { LIMITED_METHOD_CONTRACT; return (FastInterlockCompareExchangePointer( &m_managedObjectComWrapper, mocw, - NULL) == NULL); + curr) == curr); + } +#endif // !DACCESS_COMPILE + + bool TryGetExternalComObjectContext(_Out_ void** eoc) + { + LIMITED_METHOD_DAC_CONTRACT; + *eoc = m_externalComObjectContext; + return (*eoc != NULL); + } + +#ifndef DACCESS_COMPILE + bool TrySetExternalComObjectContext(_In_ void* eoc, _In_ void* curr = NULL) + { + LIMITED_METHOD_CONTRACT; + + return (FastInterlockCompareExchangePointer( + &m_externalComObjectContext, + eoc, + curr) == curr); } #endif // !DACCESS_COMPILE private: // See InteropLib API for usage. void* m_managedObjectComWrapper; + void* m_externalComObjectContext; #endif // FEATURE_COMINTEROP }; From dbfa73f97328576cba5e459abf65fb3300963783 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sun, 2 Feb 2020 11:26:04 -0800 Subject: [PATCH 19/83] Rename ExtObjCxtIterator to RuntimeCallContext. Add API for creating a Reference between an External Object and a MOW. Update the existing RCWRefCache to work with the new InteropLib API. --- src/coreclr/src/interop/inc/interoplib.h | 7 ++- .../src/interop/inc/interoplibimports.h | 12 +++- src/coreclr/src/vm/interoplibinterface.cpp | 62 +++++++++++++++---- src/coreclr/src/vm/rcwrefcache.cpp | 35 ++++++++--- src/coreclr/src/vm/rcwrefcache.h | 9 ++- 5 files changed, 98 insertions(+), 27 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 8c2edde6c2d0ae..9c84108cfb446c 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -7,8 +7,11 @@ namespace InteropLibImports { - // Forward declaration of External Object Context iterator. - class ExtObjCxtIterator; + // Forward declaration of Runtime calling context. + // This class is used by the consuming runtime to pass through details + // that may be required during a subsequent callback from the InteropLib. + // InteropLib never directly modifies or inspects supplied instances. + class RuntimeCallContext; } namespace InteropLib diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 68b489fadfe144..a5c4e84ce4a31b 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -24,10 +24,18 @@ namespace InteropLibImports // Delete Object instance handle void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; - // Get next External Object Context from the iterator. + // Get next External Object Context from the Runtime calling context. // S_OK - Context is valid. // S_FALSE - Iterator has reached end and context out parameter is set to NULL. - HRESULT IteratorNext(_In_ ExtObjCxtIterator* iter, _Outptr_result_maybenull_ void** context) noexcept; + HRESULT IteratorNext( + _In_ RuntimeCallContext* runtimeContext, + _Outptr_result_maybenull_ void** extObjContext) noexcept; + + // Create a reference between the External Object Context and the OBJECTHANDLE. + HRESULT CreateReference( + _In_ RuntimeCallContext* runtimeContext, + _In_ void* extObjContext, + _In_ InteropLib::OBJECTHANDLE handle) noexcept; // Given a ComWrappers implementation, get or create // an IReferenceTrackerTarget instance for the supplied diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 9dc5c27e489d17..93cef5d58d2664 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// Runtime headers #include "common.h" +#include "rcwrefcache.h" // Interop library header #include @@ -28,7 +30,7 @@ namespace // This class is used to track the external object within the runtime. struct ExternalObjectContext { - static DWORD InvalidSyncBlockIndex = 0; + static DWORD InvalidSyncBlockIndex = 0; // See syncblk.h static DWORD CollectedFlag = ~0; void* Identity; @@ -65,7 +67,7 @@ namespace } ExternalObjectContext* operator->() { - return (ExternalObjectContext*)_cxt; + return _cxt; } ExternalObjectContext** operator&() { @@ -135,7 +137,7 @@ namespace }; private: - friend class InteropLibImports::ExtObjCxtIterator; + friend class InteropLibImports::RuntimeCallContext; SHash _hashMap; Crst _lock; @@ -239,6 +241,8 @@ namespace // Global instance Volatile ExtObjCxtCache::g_Instance; + using ExtObjCxtRefCache = RCWRefCache; + // Defined handle types for the specific object uses. const HandleType InstanceHandleType{ HNDTYPE_STRONG }; const HandleType ComWrappersImplHandleType{ HNDTYPE_STRONG }; @@ -597,43 +601,77 @@ namespace InteropLibImports return hr; } - class ExtObjCxtIterator + class RuntimeCallContext { public: - ExtObjCxtIterator(_In_ ExtObjCxtCache* cache) + RuntimeCallContext(_In_ ExtObjCxtCache* cache, _In_ ExtObjCxtRefCache* refCache) : Curr{ cache->_hashMap.Begin() } , End{ cache->_hashMap.End() } + , RefCache{ refCache } { } + // Iterators for all known external objects. ExtObjCxtCache::Iterator Curr; ExtObjCxtCache::Iterator End; + + // Pointer to cache used to create object references. + ExtObjCxtRefCache* RefCache; }; - HRESULT IteratorNext(_In_ ExtObjCxtIterator* iter, _Outptr_result_maybenull_ void** context) noexcept + HRESULT IteratorNext( + _In_ RuntimeCallContext* runtimeContext, + _Outptr_result_maybenull_ void** extObjContext) noexcept { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; - PRECONDITION(iter != NULL); - PRECONDITION(context != NULL); + PRECONDITION(runtimeContext != NULL); + PRECONDITION(extObjContext != NULL); // Should only be called during a GC suspension PRECONDITION(Debug_IsLockedViaThreadSuspension()); } CONTRACTL_END; - if (iter->Curr == iter->End) + if (runtimeContext->Curr == runtimeContext->End) { - *context = NULL; + *extObjContext = NULL; return S_FALSE; } - ExtObjCxtCache::Element e = *iter->Curr++; - *context = e; + ExtObjCxtCache::Element e = *runtimeContext->Curr++; + *extObjContext = e; return S_OK; } + + HRESULT CreateReference( + _In_ RuntimeCallContext* runtimeContext, + _In_ void* extObjContextRaw, + _In_ InteropLib::OBJECTHANDLE handle) noexcept + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(runtimeContext != NULL); + PRECONDITION(extObjContextRaw != NULL); + PRECONDITION(handle != NULL); + + // Should only be called during a GC suspension + PRECONDITION(Debug_IsLockedViaThreadSuspension()); + } + CONTRACTL_END; + + ExternalObjectContext* extObjContext = static_cast(extObjContextRaw); + _ASSERTE(extObjContext->IsActive()); + + ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); + + return E_NOTIMPL; + } } #ifdef FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/rcwrefcache.cpp b/src/coreclr/src/vm/rcwrefcache.cpp index 652f1820e3e1d3..8b96e5658a5223 100644 --- a/src/coreclr/src/vm/rcwrefcache.cpp +++ b/src/coreclr/src/vm/rcwrefcache.cpp @@ -209,23 +209,40 @@ HRESULT RCWRefCache::AddReferenceFromRCWToCCW(RCW *pRCW, ComCallWrapper *pCCW) CONTRACTL_END; // Try adding reference using dependent handles - return AddReferenceUsingDependentHandle(pRCW, pCCW); + return AddReferenceUsingDependentHandle(pRCW->GetExposedObject(), pCCW->GetObjectRef()); } // -// Add RCW -> CCW reference using dependent handle +// Add a reference from obj1 to obj2 +// +HRESULT AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(obj1)); + PRECONDITION(CheckPointer(obj2)); + } + CONTRACTL_END; + + // Try adding reference using dependent handles + return AddReferenceUsingDependentHandle(obj1, obj2); +} + +// +// Add obj1 -> obj2 reference using dependent handle // May fail if OOM // -HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper *pCCW) +HRESULT RCWRefCache::AddReferenceUsingDependentHandle(OBJECTREF obj1, OBJECTREF obj2) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pRCW)); - PRECONDITION(CheckPointer(pCCW)); - PRECONDITION(CheckPointer(OBJECTREFToObject(pCCW->GetObjectRef()))); + PRECONDITION(CheckPointer(OBJECTREFToObject(obj2))); } CONTRACTL_END; @@ -242,7 +259,7 @@ HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper // No, we need to create a new handle EX_TRY { - OBJECTHANDLE depHnd = m_pAppDomain->CreateDependentHandle(pRCW->GetExposedObject(), pCCW->GetObjectRef()); + OBJECTHANDLE depHnd = m_pAppDomain->CreateDependentHandle(obj1, obj2); m_depHndList.Push(depHnd); STRESS_LOG2( @@ -267,8 +284,8 @@ HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper OBJECTHANDLE depHnd = (OBJECTHANDLE) m_depHndList[m_dwDepHndListFreeIndex]; IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - mgr->StoreObjectInHandle(depHnd, OBJECTREFToObject(pRCW->GetExposedObject())); - mgr->SetDependentHandleSecondary(depHnd, OBJECTREFToObject(pCCW->GetObjectRef())); + mgr->StoreObjectInHandle(depHnd, OBJECTREFToObject(obj1)); + mgr->SetDependentHandleSecondary(depHnd, OBJECTREFToObject(obj2)); STRESS_LOG3( LF_INTEROP, LL_INFO1000, diff --git a/src/coreclr/src/vm/rcwrefcache.h b/src/coreclr/src/vm/rcwrefcache.h index 4566b08429b36e..4e9aa20c23c194 100644 --- a/src/coreclr/src/vm/rcwrefcache.h +++ b/src/coreclr/src/vm/rcwrefcache.h @@ -31,6 +31,11 @@ public : // HRESULT AddReferenceFromRCWToCCW(RCW *pRCW, ComCallWrapper *pCCW); + // + // Add a reference from obj1 to obj2 + // + HRESULT AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2); + // // Enumerate all Jupiter RCWs in the RCW cache and do the callback // I'm using template here so there is no perf penality @@ -84,10 +89,10 @@ public : private : // - // Add RCW -> CCW reference using dependent handle + // Add obj1 -> obj2 reference using dependent handle // May fail if OOM // - HRESULT AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper *pCCW); + HRESULT AddReferenceUsingDependentHandle(OBJECTREF obj1, OBJECTREF obj2); private : AppDomain *m_pAppDomain; // Domain From 1073ebadd857c785aed26711a44efc888587b4d8 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sun, 2 Feb 2020 14:14:20 -0800 Subject: [PATCH 20/83] Change the CreateReference() API to an informing style API (i.e. FoundReferencePath). - This permits the runtime to make the decision on if the connection is meaningful. --- src/coreclr/src/interop/CMakeLists.txt | 1 + src/coreclr/src/interop/comwrappers.cpp | 32 +- src/coreclr/src/interop/comwrappers.h | 39 +- src/coreclr/src/interop/inc/interoplib.h | 6 +- .../src/interop/inc/interoplibimports.h | 12 +- .../src/interop/trackerobjectmanager.cpp | 549 ++++++++++++++++++ src/coreclr/src/vm/gcenv.ee.cpp | 17 +- src/coreclr/src/vm/gcenv.ee.standalone.cpp | 2 +- src/coreclr/src/vm/gcenv.ee.static.cpp | 2 +- src/coreclr/src/vm/interoplibinterface.cpp | 114 +++- src/coreclr/src/vm/interoplibinterface.h | 10 + src/coreclr/src/vm/rcwrefcache.cpp | 6 +- 12 files changed, 739 insertions(+), 51 deletions(-) create mode 100644 src/coreclr/src/interop/trackerobjectmanager.cpp diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index 268724d9ebddfa..7cb49c640f947d 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -24,6 +24,7 @@ if (WIN32) agilereferences.cpp comwrappers.cpp comwrappers.h + trackerobjectmanager.cpp referencetrackertypes.h) endif(WIN32) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 2db3a8e20d8fff..a795014b758360 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -5,6 +5,7 @@ #include "comwrappers.h" #include +using OBJECTHANDLE = InteropLib::OBJECTHANDLE; using AllocScenario = InteropLibImports::AllocScenario; namespace ABI @@ -555,9 +556,7 @@ HRESULT NativeObjectWrapperContext::Create( { hr = external->QueryInterface(&trackerObject); if (SUCCEEDED(hr)) - { - // [TODO] RETURN_IF_FAILED(TrackerRCWManager::OnIReferenceTrackerFound(trackerObject)); - } + RETURN_IF_FAILED(TrackerObjectManager::OnIReferenceTrackerFound(trackerObject)); } ComHolder reference; @@ -579,7 +578,12 @@ HRESULT NativeObjectWrapperContext::Create( { // Inform the tracker object manager _ASSERTE((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject); - // [TODO] TrackerRCWManager::AfterRCWCreated(contextLocal); + hr = TrackerObjectManager::AfterWrapperCreated(contextLocal); + if (FAILED(hr)) + { + Destroy(contextLocal); + return hr; + } } *context = contextLocal; @@ -694,18 +698,22 @@ namespace InteropLib // A caller should not be destroying a context without knowing if the context is valid. _ASSERTE(context != nullptr); + // Check if the tracker object manager should be informed prior to being destroyed. + if (context->GetReferenceTrackerFast() != nullptr) + { + // We only call this during a GC so ignore the failure as + // there is no way we can handle that failure at this point. + HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(context); + _ASSERTE(SUCCEEDED(hr)); + (void)hr; + } + NativeObjectWrapperContext::Destroy(context); } - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept // [TODO] Move to tracker object manager + bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept { - static OBJECTHANDLE g_objectHandle = nullptr; - - if (g_objectHandle != nullptr) - return false; - - g_objectHandle = objectHandle; - return true; + return TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(objectHandle); } void GetIUnknownImpl( diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 03e416ccf8e1d7..8bab0c51baced9 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -10,8 +10,6 @@ #include #include "referencetrackertypes.h" -using OBJECTHANDLE = InteropLib::OBJECTHANDLE; - enum class CreateComInterfaceFlags { None = 0, @@ -46,7 +44,7 @@ namespace ABI class ManagedObjectWrapper { public: - OBJECTHANDLE Target; + InteropLib::OBJECTHANDLE Target; private: const int32_t _runtimeDefinedCount; @@ -65,7 +63,7 @@ class ManagedObjectWrapper // Create a ManagedObjectWrapper instance static HRESULT Create( _In_ CreateComInterfaceFlags flags, - _In_ OBJECTHANDLE objectHandle, + _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, _In_ ComInterfaceEntry* userDefined, _Outptr_ ManagedObjectWrapper** mow); @@ -76,7 +74,7 @@ class ManagedObjectWrapper private: ManagedObjectWrapper( _In_ CreateComInterfaceFlags flags, - _In_ OBJECTHANDLE objectHandle, + _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, _In_ const ComInterfaceEntry* runtimeDefined, _In_ int32_t userDefinedCount, @@ -89,7 +87,7 @@ class ManagedObjectWrapper void* As(_In_ REFIID riid); // Attempt to set the target object handle based on an assumed current value. - bool TrySetObjectHandle(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current = nullptr); + bool TrySetObjectHandle(_In_ InteropLib::OBJECTHANDLE objectHandle, _In_ InteropLib::OBJECTHANDLE current = nullptr); bool IsSet(_In_ CreateComInterfaceFlags flag) const; public: // IReferenceTrackerTarget @@ -162,6 +160,35 @@ HRESULT CreateAgileReference( _In_ T* object, _Outptr_ IAgileReference** agileReference); +// Manage native object wrappers that support IReferenceTracker. +class TrackerObjectManager +{ +public: + // Attempt to set a runtime implementation for use by the IReferenceTrackerHost implementation. + static bool TrySetReferenceTrackerHostRuntimeImpl( + _In_ InteropLib::OBJECTHANDLE objectHandle, + _In_ InteropLib::OBJECTHANDLE current = nullptr); + + // Called when an IReferenceTracker instance is found. + static HRESULT OnIReferenceTrackerFound(_In_ IReferenceTracker* obj); + + // Called after wrapper has been created. + static HRESULT AfterWrapperCreated(_In_ NativeObjectWrapperContext* cxt); + + // Called before wrapper is about to be destroyed (the same lifetime as short weak handle). + static HRESULT BeforeWrapperDestroyed(_In_ NativeObjectWrapperContext* cxt); + +public: + // Called when GC started + static void OnGCStarted(_In_ int nCondemnedGeneration); + + // Called when GC finished + static void OnGCFinished(_In_ int nCondemnedGeneration); + + // Cleanup stuff when runtime is about to shutdown + static void OnShutdown(); +}; + // Class used to hold COM objects (i.e. IUnknown base class) // This class mimics the semantics of ATL::CComPtr (https://docs.microsoft.com/cpp/atl/reference/ccomptr-class). template diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 9c84108cfb446c..402adf31c65716 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -46,9 +46,9 @@ namespace InteropLib // Destroy the supplied wrapper. void DestroyWrapperForExternal(_In_ void* context) noexcept; - // Register the default callback in the Reference Tracker Host scenario. + // Register a runtime implementation callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. - bool RegisterReferenceTrackerHostCallback(_In_ OBJECTHANDLE objectHandle) noexcept; + bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept; // Get internal interop IUnknown dispatch pointers. void GetIUnknownImpl( @@ -60,6 +60,8 @@ namespace InteropLib // S_OK - the wrapper is active and the OBJECTHANDLE wasn't needed. // S_FALSE - the wrapper was inactive and the OBJECTHANDLE argument was used. HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; + + //HRESULT TrackExternalObjectReferences(_In_ RuntimeCallContext* cxt); } #endif // _WIN32 diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index a5c4e84ce4a31b..76990efe4fba89 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -21,6 +21,10 @@ namespace InteropLibImports // Free the previously allocated memory. void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept; + HRESULT AddMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept; + HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept; + //HRESULT RequestGarbageCollectionForExternal() noexcept; + // Delete Object instance handle void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; @@ -31,13 +35,14 @@ namespace InteropLibImports _In_ RuntimeCallContext* runtimeContext, _Outptr_result_maybenull_ void** extObjContext) noexcept; - // Create a reference between the External Object Context and the OBJECTHANDLE. - HRESULT CreateReference( + // Tell the runtime a reference path between the External Object Context and + // OBJECTHANDLE was found. + HRESULT FoundReferencePath( _In_ RuntimeCallContext* runtimeContext, _In_ void* extObjContext, _In_ InteropLib::OBJECTHANDLE handle) noexcept; - // Given a ComWrappers implementation, get or create + // Given a runtime implementation, get or create // an IReferenceTrackerTarget instance for the supplied // external object. HRESULT GetOrCreateTrackerTargetForExternal( @@ -46,6 +51,7 @@ namespace InteropLibImports _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, _Outptr_ void** trackerTarget) noexcept; + } #endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp new file mode 100644 index 00000000000000..b1dd437fc68c91 --- /dev/null +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -0,0 +1,549 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Runtime headers +#include + +#include "comwrappers.h" +#include + +using OBJECTHANDLE = InteropLib::OBJECTHANDLE; + +namespace +{ + const IID IID_IReferenceTrackerHost = __uuidof(IReferenceTrackerHost); + const IID IID_IReferenceTrackerTarget = __uuidof(IReferenceTrackerTarget); + const IID IID_IReferenceTracker = __uuidof(IReferenceTracker); + const IID IID_IReferenceTrackerManager = __uuidof(IReferenceTrackerManager); + const IID IID_IFindReferenceTargetsCallback = __uuidof(IFindReferenceTargetsCallback); + + // In order to minimize the impact of a constructor running on module load, + // the HostServices class should have no instance fields. + class HostServices : public IReferenceTrackerHost + { + public: // static + static Volatile RuntimeImpl; + + public: // IReferenceTrackerHost + STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags); + STDMETHOD(ReleaseDisconnectedReferenceSources)(); + STDMETHOD(NotifyEndOfReferenceTrackingOnThread)(); + STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference); + STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated); + STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated); + + public: // IUnknown + // Lifetime maintained by stack - we don't care about ref counts + STDMETHOD_(ULONG, AddRef)() { return 1; } + STDMETHOD_(ULONG, Release)() { return 1; } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + if (ppvObject == nullptr) + return E_POINTER; + + if (IsEqualIID(riid, IID_IReferenceTrackerHost)) + { + *ppvObject = static_cast(this); + } + else if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + (void)AddRef(); + return S_OK; + } + }; + + // Runtime implementation for some host services. + Volatile HostServices::RuntimeImpl; + + // Global instance of host services. + HostServices g_HostServicesInstance; + + // Defined in windows.ui.xaml.hosting.referencetracker.h. + enum XAML_REFERENCETRACKER_DISCONNECT + { + // Indicates the disconnect is during a suspend and a GC can be trigger. + XAML_REFERENCETRACKER_DISCONNECT_SUSPEND = 0x00000001 + }; + + STDMETHODIMP HostServices::DisconnectUnusedReferenceSources(_In_ DWORD flags) + { + if (flags & XAML_REFERENCETRACKER_DISCONNECT_SUSPEND) + { + } + + return E_NOTIMPL; // [TODO] + } + + STDMETHODIMP HostServices::ReleaseDisconnectedReferenceSources() + { + return E_NOTIMPL; // [TODO] + } + + // + // Release context-bound RCWs and Jupiter RCWs (which are free-threaded but context-bound) + // in the current apartment + // + STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread() + { + return E_NOTIMPL; // [TODO] + } + + // + // Creates a proxy object that points to the given RCW + // The proxy + // 1. Has a managed reference pointing to the RCW, and therefore forms a cycle that can be resolved by GC + // 2. Forwards data binding requests + // For example: + // + // Grid <---- RCW Grid <-------- RCW + // | ^ | ^ + // | | Becomes | | + // v | v | + // Rectangle Rectangle ----->Proxy + // + // Arguments + // obj - The identity IUnknown* where a RCW points to (Grid, in this case) + // Note that + // 1) we can either create a new RCW or get back an old one from cache + // 2) This obj could be a regular WinRT object (such as WinRT collection) for data binding + // ppNewReference - The IReferenceTrackerTarget* for the proxy created + // Jupiter will call IReferenceTrackerTarget to establish a jupiter reference + // + STDMETHODIMP HostServices::GetTrackerTarget(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) + { + if (obj == nullptr || ppNewReference == nullptr) + return E_INVALIDARG; + + HRESULT hr; + + OBJECTHANDLE impl = HostServices::RuntimeImpl; + if (impl == nullptr) + return E_NOT_SET; + + // QI for IUnknown to get the identity unknown + ComHolder identity; + RETURN_IF_FAILED(obj->QueryInterface(&identity)); + + // Get or create an existing implementation for the this external. + ComHolder target; + RETURN_IF_FAILED(GetOrCreateTrackerTargetForExternal( + impl, + identity, + CreateRCWFlags::TrackerObject, + CreateCCWFlags::TrackerSupport, + (void**)&target)); + + return target->QueryInterface(IID_IReferenceTrackerTarget, (void**)ppNewReference); + } + + STDMETHODIMP HostServices::AddMemoryPressure(_In_ UINT64 bytesAllocated) + { + return InteropLibImports::AddMemoryPressureForExternal(bytesAllocated); + } + + STDMETHODIMP HostServices::RemoveMemoryPressure(_In_ UINT64 bytesAllocated) + { + return InteropLibImports::RemoveMemoryPressureForExternal(bytesAllocated); + } +} + +namespace +{ + // [TODO] + Volatile s_TrackerManager; // The one and only Tracker Manager instance + Volatile s_IsGCStarted = FALSE; + + // + // Tells GC whether walking all the Jupiter RCW is necessary, which only should happen + // if we have seen jupiter RCWs + // [TODO] + BOOL NeedToWalkRCWs() + { + return (s_TrackerManager.load() != nullptr); + } + + // + // Callback implementation of IFindReferenceTargetsCallback + // [TODO] + class FindDependentWrappersCallback : public IFindReferenceTargetsCallback + { + public: + FindDependentWrappersCallback(_In_ RCWInstance* rcw, _In_ TrackerRCWEnum* rcwEnum) + : _rcw{ rcw } + , _rcwEnum{ rcwEnum } + { + } + + STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) + { + assert(target != nullptr); + + HRESULT hr; + + CCW* ccw = CCW::MapIUnknownToWrapper(target); + + // Not a target we implemented. + if (ccw == nullptr) + return S_OK; + + // + // Skip dependent handle creation if RCW/CCW points to the same managed object + // + if (_rcw->GetObjectGCHandle() == ccw->GetObjectGCHandle()) + return S_OK; + + // + // Jupiter might return CCWs with outstanding references that are either : + // 1. Neutered - in this case it is unsafe to touch m_ppThis + // 2. RefCounted handle NULLed out by GC + // + // Skip those to avoid crashes + // + if (!ccw->IsAlive()) + return S_OK; + + // + // Add a reference from rcw -> ccw so that GC knows about this reference + // + RETURN_IF_FAILED(_rcwEnum->AddReferenceFromRCWToCCW(_rcw, ccw)); + + return S_OK; + } + + // Lifetime maintained by stack - we don't care about ref counts + STDMETHOD_(ULONG, AddRef)() { return 1; } + STDMETHOD_(ULONG, Release)() { return 1; } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + if (ppvObject == nullptr) + return E_POINTER; + + if (IsEqualIID(riid, IID_IFindReferenceTargetsCallback)) + { + *ppvObject = static_cast(this); + } + else if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + (void)AddRef(); + return S_OK; + } + + private: + RCWInstance* _rcw; + TrackerRCWEnum* _rcwEnum; + }; + + // + // Ask Jupiter all the CCWs referenced (through native code) by this RCW and build reference for RCW -> CCW + // so that GC knows about this reference + // [TODO] + HRESULT WalkOneRCW(_In_ RCWInstance* rcw, _In_ TrackerRCWEnum* rcwEnum) + { + assert(rcw != nullptr && rcwEnum != nullptr); + + HRESULT hr; + + // Get IReferenceTracker * from RCW - we can call IReferenceTracker* from any thread and it won't be a proxy + ComHolder obj; + RETURN_IF_FAILED(rcw->GetInstanceProxy(&obj)); + + FindDependentWrappersCallback cb{ rcw, rcwEnum }; + RETURN_IF_FAILED(obj->FindTrackerTargets(&cb)); + + return S_OK; + } +} + +void TrackerRCWManager::OnGCStartedWorker() +{ + // Due to the nesting GCStart/GCEnd pairs (see comment for this function), we need to check + // those flags inside nCondemnedGeneration >= 2 check + assert(s_IsGCStarted == FALSE); + assert(s_IsGlobalPeggingOn == TRUE); + + s_IsGCStarted = TRUE; + + // + // Let Jupiter know we are about to walk RCWs so that they can lock their reference cache + // Note that Jupiter doesn't need to unpeg all CCWs at this point and they can do the pegging/unpegging in FindTrackerTargetsCompleted + // + assert(s_TrackerManager.load() != nullptr); + s_TrackerManager.load()->ReferenceTrackingStarted(); + + // From this point, jupiter decides whether a CCW should be pegged or not as global pegging flag is now off + s_IsGlobalPeggingOn = FALSE; + + // + // OK. Time to walk all the reference RCWs + // + WalkRCWs(); +} + +void TrackerRCWManager::OnGCFinishedWorker() +{ + // + // Let Jupiter know RCW walk is done and they need to: + // 1. Unpeg all CCWs if the CCW needs to be unpegged (when the CCW is only reachable by other jupiter RCWs) + // 2. Peg all CCWs if the CCW needs to be pegged (when the above condition is not true) + // 3. Unlock reference cache when they are done + // + // If the walk has failed - Jupiter doesn't need to do anything and could just return immediately + // + assert(s_TrackerManager.load() != nullptr); + s_TrackerManager.load()->ReferenceTrackingCompleted(); + + s_IsGlobalPeggingOn = TRUE; + s_IsGCStarted = FALSE; +} + +// +// Walk all the jupiter RCWs in all AppDomains and build references from RCW -> CCW as we go +// +void TrackerRCWManager::WalkRCWs() +{ + BOOL walkFailed = FALSE; + HRESULT hr; + + try + { + TrackerRCWEnum* rcwEnum = TrackerRCWEnum::GetInstance(); + assert(rcwEnum != nullptr); + + // + // Reset the cache + // + rcwEnum->ResetDependentHandles(); + + // + // Enumerate Jupiter RCWs + // + hr = rcwEnum->EnumerateJupiterRCWs(WalkOneRCW); + + // + // Shrink the dependent handle cache if necessary and clear unused handles. + // + rcwEnum->ShrinkDependentHandles(); + } + catch (...) + { + hr = E_FAIL; + } + + if (FAILED(hr)) + { + // Remember the fact that we've failed and stop walking + walkFailed = TRUE; + s_IsGlobalPeggingOn = TRUE; + } + + // + // Let Jupiter know RCW walk is done and they need to: + // 1. Unpeg all CCWs if the CCW needs to be unpegged (when the CCW is only reachable by other jupiter RCWs) + // 2. Peg all CCWs if the CCW needs to be pegged (when the above condition is not true) + // 3. Unlock reference cache when they are done + // + // If the walk has failed - Jupiter doesn't need to do anything and could just return immediately + // + assert(s_TrackerManager.load() != nullptr); + s_TrackerManager.load()->FindTrackerTargetsCompleted(walkFailed); +} + +bool TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current) +{ + // Attempt to set the runtime implementation for providing hosting services to the tracker runtime. + return (::InterlockedCompareExchangePointer( + &HostServices::RuntimeImpl, + objectHandle, current) == current); +} + +HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* obj) +{ + _ASSERTE(obj != nullptr); + if (s_TrackerManager.load() != nullptr) + return S_OK; + + // Retrieve IReferenceTrackerManager + HRESULT hr; + ComHolder trackerManager; + RETURN_IF_FAILED(obj->GetReferenceTrackerManager(&trackerManager)); + + ComHolder clrServices; + RETURN_IF_FAILED(g_HostServicesInstance.QueryInterface(IID_IReferenceTrackerHost, (void**)&clrServices)); + + // [TODO] Temporarily switch back to coop and disable GC to avoid racing with the very first RCW walk + //GCX_COOP(); + //GCX_FORBID(); + + IReferenceTrackerManager* expected = nullptr; + if (s_TrackerManager.compare_exchange_strong(expected, trackerManager.p)) + { + (void)trackerManager.Detach(); // Ownership has been transfered + + // + // OK. It is time to do our initialization + // It's safe to do it here because we are in COOP and only one thread wins the race + // + RETURN_IF_FAILED(s_TrackerManager.load()->SetReferenceTrackerHost(clrServices)); + } + + return S_OK; +} + +HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ NativeObjectWrapperContext* cxt) +{ + _ASSERTE(cxt != nullptr); + + HRESULT hr; + IReferenceTracker* obj = cxt->GetReferenceTrackerFast(); + _ASSERTE(obj != nullptr); + + // Notify tracker runtime that we've created a new wrapper for this object. + // To avoid surprises, we should notify them before we fire the first AddRefFromTrackerSource. + RETURN_IF_FAILED(obj->ConnectFromTrackerSource()); + + // Send out AddRefFromTrackerSource callbacks to notify tracker runtime we've done AddRef() + // for certain interfaces. We should do this *after* we made a AddRef() because we should never + // be in a state where report refs > actual refs + RETURN_IF_FAILED(obj->AddRefFromTrackerSource()); + + return S_OK; +} + +HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ NativeObjectWrapperContext* cxt) +{ + _ASSERTE(cxt != nullptr); + + HRESULT hr; + ComHolder obj; + RETURN_IF_FAILED(cxt->GetInstanceProxy(&obj)); + + // Notify tracker runtime that we are about to destroy a wrapper + // (same timing as short weak handle) for this object. + // They need this information to disconnect weak refs and stop firing events, + // so that they can avoid resurrecting the object. + RETURN_IF_FAILED(obj->DisconnectFromTrackerSource()); + + return S_OK; +} + +namespace +{ + // + // We never expect exceptions to be thrown outside of RCWWalker + // So make sure we fail fast here, instead of going through normal + // exception processing and fail later + // This will make analyzing dumps much easier + // + LONG RCWWalker_UnhandledExceptionFilter(DWORD code, _In_ EXCEPTION_POINTERS* excep) + { + if ((excep->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) + || (excep->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)) + { + // We don't want to fail fast on debugger exceptions + return EXCEPTION_CONTINUE_SEARCH; + } + + assert(false); + std::abort(); + + return EXCEPTION_EXECUTE_HANDLER; + } + + using OnGCEventProc = void(*)(); + void SetupFailFastFilterAndCall(_In_ OnGCEventProc func) + { + __try + { + // Call the internal worker function which has the runtime contracts + func(); + } + __except (RCWWalker_UnhandledExceptionFilter(GetExceptionCode(), GetExceptionInformation())) + { + assert(false && "Should not get here"); + } + } +} + +// +// Note that we could get nested GCStart/GCEnd calls, such as : +// GCStart for Gen 2 background GC +// GCStart for Gen 0/1 foregorund GC +// GCEnd for Gen 0/1 foreground GC +// .... +// GCEnd for Gen 2 background GC +// +// The nCondemnedGeneration >= 2 check takes care of this nesting problem +// +void TrackerObjectManager::OnGCStarted(_In_ int nCondemnedGeneration) +{ + if (nCondemnedGeneration < 2) // We are only doing walk in Gen2 GC + return; + + if (!NeedToWalkRCWs()) // Have we seen Jupiter RCWs? + return; + + // Make sure we fail fast if anything goes wrong when we interact with Jupiter + SetupFailFastFilterAndCall(TrackerObjectManager::OnGCStartedWorker); +} + +// +// Note that we could get nested GCStart/GCEnd calls, such as : +// GCStart for Gen 2 background GC +// GCStart for Gen 0/1 foregorund GC +// GCEnd for Gen 0/1 foreground GC +// .... +// GCEnd for Gen 2 background GC +// +// The nCondemnedGeneration >= 2 check takes care of this nesting problem +// +void TrackerObjectManager::OnGCFinished(_In_ int nCondemnedGeneration) +{ + // + // Note that we need to check in both OnGCFinished and OnGCStarted + // As there could be multiple OnGCFinished with nCondemnedGeneration < 2 in the case of Gen 2 GC + // + // Also, if this is background GC, the NeedToWalkRCWs predicate may change from FALSE to TRUE while + // the GC is running. We don't want to do any work if it's the case (i.e. if s_IsGCStarted is FALSE). + // + if (nCondemnedGeneration >= 2 // We are only doing walk in Gen2 GC + && NeedToWalkRCWs() // Have we seen Jupiter RCWs? + && s_IsGCStarted == TRUE) // Had we seen Jupiter RCWs when the GC started? + { + // Make sure we fail fast if anything goes wrong when we interact with Jupiter + SetupFailFastFilterAndCall(TrackerObjectManager::OnGCFinishedWorker); + } +} + +void TrackerObjectManager::OnShutdown() +{ + IReferenceTrackerManager* trackerManager = s_TrackerManager.exchange(nullptr); + if (trackerManager != nullptr) + { + // Make sure s_TrackerManager is always either null or a valid IReferenceTrackerManager * + // this will make crash easier to diagnose + trackerManager->Release(); + } +} diff --git a/src/coreclr/src/vm/gcenv.ee.cpp b/src/coreclr/src/vm/gcenv.ee.cpp index fe0fc30575d236..8352bb66f37557 100644 --- a/src/coreclr/src/vm/gcenv.ee.cpp +++ b/src/coreclr/src/vm/gcenv.ee.cpp @@ -214,17 +214,7 @@ void GCToEEInterface::GcStartWork (int condemned, int max_gen) #endif #ifdef FEATURE_COMINTEROP - // - // Let GC detect managed/native cycles with input from jupiter - // Jupiter will - // 1. Report reference from RCW to CCW based on native reference in Jupiter - // 2. Identify the subset of CCWs that needs to be rooted - // - // We'll build the references from RCW to CCW using - // 1. Preallocated arrays - // 2. Dependent handles - // - RCWWalker::OnGCStarted(condemned); + Interop::OnGCStarted(condemned); #endif // FEATURE_COMINTEROP if (condemned == max_gen) @@ -243,10 +233,7 @@ void GCToEEInterface::GcDone(int condemned) CONTRACTL_END; #ifdef FEATURE_COMINTEROP - // - // Tell Jupiter GC has finished - // - RCWWalker::OnGCFinished(condemned); + Interop::OnGCFinished(condemned); #endif // FEATURE_COMINTEROP } diff --git a/src/coreclr/src/vm/gcenv.ee.standalone.cpp b/src/coreclr/src/vm/gcenv.ee.standalone.cpp index 85f6a698d2cde2..5d6a58dd776a03 100644 --- a/src/coreclr/src/vm/gcenv.ee.standalone.cpp +++ b/src/coreclr/src/vm/gcenv.ee.standalone.cpp @@ -10,7 +10,7 @@ #ifdef FEATURE_COMINTEROP #include "runtimecallablewrapper.h" -#include "rcwwalker.h" +#include "interoplibinterface.h" #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/gcenv.ee.static.cpp b/src/coreclr/src/vm/gcenv.ee.static.cpp index e04fd58ae6e8a3..697fed5b56980d 100644 --- a/src/coreclr/src/vm/gcenv.ee.static.cpp +++ b/src/coreclr/src/vm/gcenv.ee.static.cpp @@ -10,7 +10,7 @@ #ifdef FEATURE_COMINTEROP #include "runtimecallablewrapper.h" -#include "rcwwalker.h" +#include "interoplibinterface.h" #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 93cef5d58d2664..03512385e093fd 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -5,6 +5,7 @@ // Runtime headers #include "common.h" #include "rcwrefcache.h" +#include "rcwwalker.h" // Interop library header #include @@ -30,8 +31,8 @@ namespace // This class is used to track the external object within the runtime. struct ExternalObjectContext { - static DWORD InvalidSyncBlockIndex = 0; // See syncblk.h - static DWORD CollectedFlag = ~0; + static DWORD InvalidSyncBlockIndex; + static DWORD CollectedFlag; void* Identity; DWORD SyncBlockIndex; @@ -51,6 +52,9 @@ namespace } }; + DWORD ExternalObjectContext::InvalidSyncBlockIndex = 0; // See syncblk.h + DWORD ExternalObjectContext::CollectedFlag = ~0; + static_assert((sizeof(ExternalObjectContext) % sizeof(void*)) == 0, "Keep context pointer size aligned"); // Holder for a External Object Context @@ -528,6 +532,49 @@ namespace InteropLibImports ::free(mem); } + HRESULT AddMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept + { + CONTRACTL + { + NOTHROW; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + GCInterface::NewAddMemoryPressure(memoryInBytes); + } + END_EXTERNAL_ENTRYPOINT; + + return hr; + } + + HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept + { + CONTRACTL + { + NOTHROW; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + GCInterface::NewRemoveMemoryPressure(memoryInBytes); + } + END_EXTERNAL_ENTRYPOINT; + + return hr; + } + + void RequestGarbageCollection() noexcept + { + + } + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept { CONTRACTL @@ -646,7 +693,7 @@ namespace InteropLibImports return S_OK; } - HRESULT CreateReference( + HRESULT FoundReferencePath( _In_ RuntimeCallContext* runtimeContext, _In_ void* extObjContextRaw, _In_ InteropLib::OBJECTHANDLE handle) noexcept @@ -665,12 +712,20 @@ namespace InteropLibImports } CONTRACTL_END; + // Get the external object's managed wrapper ExternalObjectContext* extObjContext = static_cast(extObjContextRaw); _ASSERTE(extObjContext->IsActive()); + OBJECTREF source = ObjectToOBJECTREF(g_pSyncTable[extObjContext->SyncBlockIndex].m_Object); + // Get the target of the external object's reference. ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); + OBJECTREF target = ObjectFromHandle(objectHandle); + + // If these point at the same object don't create a reference. + if (source->GetSyncBlock() == target->GetSyncBlock()) + return S_FALSE; - return E_NOTIMPL; + return runtimeContext->RefCache->AddReferenceFromObjectToObject(source, target); } } @@ -761,7 +816,7 @@ void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( implHandle = GetAppDomain()->CreateTypedHandle(implRef, ComWrappersImplHandleType); - if (!InteropLib::Com::RegisterReferenceTrackerHostCallback(implHandle)) + if (!InteropLib::Com::RegisterReferenceTrackerHostRuntimeImpl(implHandle)) { DestroyHandleCommon(implHandle, ComWrappersImplHandleType); COMPlusThrow(kInvalidOperationException, IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS); @@ -804,13 +859,13 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) InteropLib::Com::DestroyWrapperForObject(wrapper); } -void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* context) +void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) { CONTRACTL { NOTHROW; MODE_ANY; - PRECONDITION(context != NULL); + PRECONDITION(contextRaw != NULL); } CONTRACTL_END; @@ -829,7 +884,7 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(context != NULL); + PRECONDITION(contextRaw != NULL); PRECONDITION(GCHeapUtilities::IsGCInProgress()); } CONTRACTL_END; @@ -844,3 +899,46 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context } #endif // FEATURE_COMINTEROP + +void Interop::OnGCStarted(_In_ int nCondemnedGeneration) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + // + // Let GC detect managed/native cycles with input from jupiter + // Jupiter will + // 1. Report reference from RCW to CCW based on native reference in Jupiter + // 2. Identify the subset of CCWs that needs to be rooted + // + // We'll build the references from RCW to CCW using + // 1. Preallocated arrays + // 2. Dependent handles + // + RCWWalker::OnGCStarted(nCondemnedGeneration); + +#endif // FEATURE_COMINTEROP +} + +void Interop::OnGCFinished(_In_ int nCondemnedGeneration) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + // + // Tell Jupiter GC has finished + // + RCWWalker::OnGCFinished(nCondemnedGeneration); + +#endif // FEATURE_COMINTEROP +} diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 8747912f4bcc14..70ffe6a17774f5 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -38,3 +38,13 @@ class ComWrappersNative }; #endif // FEATURE_COMINTEROP + +class Interop +{ +public: + // Notify when GC started + static void OnGCStarted(_In_ int nCondemnedGeneration); + + // Notify when GC finished + static void OnGCFinished(_In_ int nCondemnedGeneration); +}; diff --git a/src/coreclr/src/vm/rcwrefcache.cpp b/src/coreclr/src/vm/rcwrefcache.cpp index 8b96e5658a5223..c22a84e6816a55 100644 --- a/src/coreclr/src/vm/rcwrefcache.cpp +++ b/src/coreclr/src/vm/rcwrefcache.cpp @@ -215,15 +215,15 @@ HRESULT RCWRefCache::AddReferenceFromRCWToCCW(RCW *pRCW, ComCallWrapper *pCCW) // // Add a reference from obj1 to obj2 // -HRESULT AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2) +HRESULT RCWRefCache::AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; - PRECONDITION(CheckPointer(obj1)); - PRECONDITION(CheckPointer(obj2)); + PRECONDITION(obj1 != NULL); + PRECONDITION(obj2 != NULL); } CONTRACTL_END; From 0d80d43f137691524b3a3c06d1b4965dea07ef93 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 4 Feb 2020 19:01:58 -0800 Subject: [PATCH 21/83] Make global pegging field on RCWWalker public. --- src/coreclr/src/vm/rcwwalker.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/rcwwalker.h b/src/coreclr/src/vm/rcwwalker.h index 0a532d8830ef80..f6257259b7057b 100644 --- a/src/coreclr/src/vm/rcwwalker.h +++ b/src/coreclr/src/vm/rcwwalker.h @@ -33,12 +33,13 @@ class RCWWalker { friend struct _DacGlobals; -private : +private: static VolatilePtr s_pGCManager; // The one and only GCManager instance static BOOL s_bGCStarted; // Has GC started? + +public: SVAL_DECL(BOOL, s_bIsGlobalPeggingOn); // Do we need to peg every CCW? -public : #ifndef DACCESS_COMPILE static void OnJupiterRCWCreated(RCW *pRCW, IJupiterObject *pJupiterObject); static void AfterJupiterRCWCreated(RCW *pRCW); From 152a5e4be5382453423d6bbf8fd58e19667392ca Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 4 Feb 2020 19:13:57 -0800 Subject: [PATCH 22/83] Place all exported functions into a single compilation unit for InteropLib. Add Begin/End reference tracking APIs. Expose Global Pegging value to InteropLib. --- src/coreclr/src/interop/CMakeLists.txt | 1 + src/coreclr/src/interop/comwrappers.cpp | 142 +------- src/coreclr/src/interop/comwrappers.h | 22 +- src/coreclr/src/interop/inc/interoplib.h | 23 +- .../src/interop/inc/interoplibimports.h | 13 +- src/coreclr/src/interop/interoplib.cpp | 146 ++++++++ src/coreclr/src/interop/platform.h | 1 + .../src/interop/trackerobjectmanager.cpp | 326 +++++++----------- src/coreclr/src/vm/ceemain.cpp | 2 +- src/coreclr/src/vm/interoplibinterface.cpp | 123 +++++-- src/coreclr/src/vm/interoplibinterface.h | 3 + 11 files changed, 436 insertions(+), 366 deletions(-) create mode 100644 src/coreclr/src/interop/interoplib.cpp diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index 7cb49c640f947d..af5f448d297857 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -2,6 +2,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(BEFORE inc) set(INTEROP_COMMON_SOURCES + interoplib.cpp ) set(INTEROP_COMMON_HEADERS diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index a795014b758360..796162cbbd3e2d 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -277,7 +277,21 @@ namespace static_assert(sizeof(ManagedObjectWrapper_IReferenceTrackerTargetImpl) == (7 * sizeof(void*)), "Unexpected vtable size"); } -ManagedObjectWrapper* ManagedObjectWrapper::MapIUnknownToWrapper(_In_ IUnknown* pUnk) +void ManagedObjectWrapper::GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) +{ + _ASSERTE(fpQueryInterface != nullptr + && fpAddRef != nullptr + && fpRelease != nullptr); + + *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; + *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; + *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; +} + +ManagedObjectWrapper* ManagedObjectWrapper::MapFromIUnknown(_In_ IUnknown* pUnk) { _ASSERTE(pUnk != nullptr); @@ -507,7 +521,10 @@ ULONG ManagedObjectWrapper::Release(void) { OBJECTHANDLE local = Target; ULONG refCount = (ULONG)::InterlockedDecrement64(&_refCount); - if (refCount == 0) + + // It is possible that a target wasn't set during an + // attempt to reactive the wrapper. + if (refCount == 0 && local != nullptr) { // Attempt to reset the target if its current value is the same. // It is possible the wrapper is in the middle of being reactivated. @@ -626,124 +643,3 @@ IReferenceTracker* NativeObjectWrapperContext::GetReferenceTrackerFast() const { return _trackerObject; } - -namespace InteropLib -{ - namespace Com - { - HRESULT CreateWrapperForObject( - _In_ OBJECTHANDLE instance, - _In_ INT32 vtableCount, - _In_ void* vtablesRaw, - _In_ INT32 flagsRaw, - _Outptr_ IUnknown** wrapper) noexcept - { - if (instance == nullptr || (vtablesRaw == nullptr && vtableCount != 0) || vtableCount < 0) - return E_INVALIDARG; - - if (wrapper == nullptr) - return E_POINTER; - - HRESULT hr; - - // Convert inputs to appropriate types. - auto flags = static_cast(flagsRaw); - auto vtables = static_cast(vtablesRaw); - - ManagedObjectWrapper* mow; - RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow)); - - *wrapper = static_cast(mow->As(IID_IUnknown)); - return S_OK; - } - - void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(static_cast(wrapperMaybe)); - - // A caller should not be destroying a wrapper without knowing if the wrapper is valid. - _ASSERTE(wrapper != nullptr); - - ManagedObjectWrapper::Destroy(wrapper); - } - - HRESULT CreateWrapperForExternal( - _In_ IUnknown* external, - _In_ INT32 flagsRaw, - _In_ size_t contextSize, - _Outptr_ void** context) noexcept - { - if (external == nullptr) - return E_INVALIDARG; - - if (context == nullptr) - return E_POINTER; - - HRESULT hr; - - // Convert input to appropriate type. - auto flags = static_cast(flagsRaw); - - NativeObjectWrapperContext* wrapperContext; - RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); - - *context = wrapperContext->GetRuntimeContext(); - return S_OK; - } - - void DestroyWrapperForExternal(_In_ void* contextMaybe) noexcept - { - NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); - - // A caller should not be destroying a context without knowing if the context is valid. - _ASSERTE(context != nullptr); - - // Check if the tracker object manager should be informed prior to being destroyed. - if (context->GetReferenceTrackerFast() != nullptr) - { - // We only call this during a GC so ignore the failure as - // there is no way we can handle that failure at this point. - HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(context); - _ASSERTE(SUCCEEDED(hr)); - (void)hr; - } - - NativeObjectWrapperContext::Destroy(context); - } - - bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept - { - return TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(objectHandle); - } - - void GetIUnknownImpl( - _Out_ void** fpQueryInterface, - _Out_ void** fpAddRef, - _Out_ void** fpRelease) noexcept - { - _ASSERTE(fpQueryInterface != nullptr - && fpAddRef != nullptr - && fpRelease != nullptr); - - *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface; - *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef; - *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release; - } - - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapIUnknownToWrapper(wrapperMaybe); - if (wrapper == nullptr || handle == nullptr) - return E_INVALIDARG; - - ULONG count = wrapper->AddRef(); - if (count == 1) - { - ::InterlockedExchangePointer(&wrapper->Target, handle); - return S_FALSE; - } - - return S_OK; - } - } -} diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 8bab0c51baced9..64618b0e7c50e6 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -6,7 +6,6 @@ #define _INTEROP_COMWRAPPERS_H_ #include "platform.h" -#include // COM interfaces #include #include "referencetrackertypes.h" @@ -57,8 +56,15 @@ class ManagedObjectWrapper const CreateComInterfaceFlags _flags; public: // static - // Convert the IUnknown if the instance is a ManagedObjectWrapper into a ManagedObjectWrapper, otherwise null. - static ManagedObjectWrapper* MapIUnknownToWrapper(_In_ IUnknown* pUnk); + // Get the implementation for IUnknown. + static void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease); + + // Convert the IUnknown if the instance is a ManagedObjectWrapper + // into a ManagedObjectWrapper, otherwise null. + static ManagedObjectWrapper* MapFromIUnknown(_In_ IUnknown* pUnk); // Create a ManagedObjectWrapper instance static HRESULT Create( @@ -179,13 +185,13 @@ class TrackerObjectManager static HRESULT BeforeWrapperDestroyed(_In_ NativeObjectWrapperContext* cxt); public: - // Called when GC started - static void OnGCStarted(_In_ int nCondemnedGeneration); + // Begin the reference tracking process for external objects. + static HRESULT BeginReferenceTracking(InteropLibImports::RuntimeCallContext* cxt); - // Called when GC finished - static void OnGCFinished(_In_ int nCondemnedGeneration); + // End the reference tracking process for external object. + static HRESULT EndReferenceTracking(); - // Cleanup stuff when runtime is about to shutdown + // Clean up internal resources used for reference tracking. static void OnShutdown(); }; diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 402adf31c65716..2c75f2cad9751d 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -11,13 +11,16 @@ namespace InteropLibImports // This class is used by the consuming runtime to pass through details // that may be required during a subsequent callback from the InteropLib. // InteropLib never directly modifies or inspects supplied instances. - class RuntimeCallContext; + struct RuntimeCallContext; } namespace InteropLib { using OBJECTHANDLE = void*; + // Clean up the interop library. + void Shutdown() noexcept; + #ifdef _WIN32 namespace Com @@ -57,15 +60,21 @@ namespace InteropLib _Out_ void** fpRelease) noexcept; // Ensure the wrapper is active and take an AddRef. - // S_OK - the wrapper is active and the OBJECTHANDLE wasn't needed. - // S_FALSE - the wrapper was inactive and the OBJECTHANDLE argument was used. - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; - - //HRESULT TrackExternalObjectReferences(_In_ RuntimeCallContext* cxt); + // S_OK - the wrapper is active and the OBJECTHANDLE wasn't needed. + // S_FALSE - the wrapper was inactive and the OBJECTHANDLE argument was used. + // E_HANDLE - the supplied wrapper is inactive and an OBJECTHANDLE wasn't supplied. + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_opt_ OBJECTHANDLE handle) noexcept; + + // Begin the reference tracking process on external COM objects. + // This should only be called during a runtime's GC phase. + HRESULT BeginExternalObjectReferenceTracking(_In_ InteropLibImports::RuntimeCallContext* cxt) noexcept; + + // End the reference tracking process. + // This should only be called during a runtime's GC phase. + HRESULT EndExternalObjectReferenceTracking() noexcept; } #endif // _WIN32 - } #endif // _INTEROP_INC_INTEROPLIB_H_ diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 76990efe4fba89..7c82376d8f9195 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -21,13 +21,23 @@ namespace InteropLibImports // Free the previously allocated memory. void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept; + // Add memory pressure to the runtime's GC calculations. HRESULT AddMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept; + + // Remove memory pressure from the runtime's GC calculations. HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept; - //HRESULT RequestGarbageCollectionForExternal() noexcept; + + // [TODO] HRESULT RequestGarbageCollectionForExternal() noexcept; // Delete Object instance handle void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; + // Get the current global pegging state. + bool GetGlobalPeggingState() noexcept; + + // Set the current global pegging state. + void SetGlobalPeggingState(_In_ bool state) noexcept; + // Get next External Object Context from the Runtime calling context. // S_OK - Context is valid. // S_FALSE - Iterator has reached end and context out parameter is set to NULL. @@ -51,7 +61,6 @@ namespace InteropLibImports _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, _Outptr_ void** trackerTarget) noexcept; - } #endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_ diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp new file mode 100644 index 00000000000000..fe5129e48cb611 --- /dev/null +++ b/src/coreclr/src/interop/interoplib.cpp @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "platform.h" +#include + +#include "comwrappers.h" + +using OBJECTHANDLE = InteropLib::OBJECTHANDLE; +using RuntimeCallContext = InteropLibImports::RuntimeCallContext; + +namespace InteropLib +{ + void Shutdown() noexcept + { + TrackerObjectManager::OnShutdown(); + } + + // Exposed COM related API + namespace Com + { + HRESULT CreateWrapperForObject( + _In_ OBJECTHANDLE instance, + _In_ INT32 vtableCount, + _In_ void* vtablesRaw, + _In_ INT32 flagsRaw, + _Outptr_ IUnknown** wrapper) noexcept + { + _ASSERTE(instance != nullptr && wrapper != nullptr); + + // Validate the supplied vtable data is valid with a + // reasonable count. + if ((vtablesRaw == nullptr && vtableCount != 0) || vtableCount < 0) + return E_INVALIDARG; + + HRESULT hr; + + // Convert inputs to appropriate types. + auto flags = static_cast(flagsRaw); + auto vtables = static_cast(vtablesRaw); + + ManagedObjectWrapper* mow; + RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow)); + + *wrapper = static_cast(mow->As(IID_IUnknown)); + return S_OK; + } + + void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(static_cast(wrapperMaybe)); + + // A caller should not be destroying a wrapper without knowing if the wrapper is valid. + _ASSERTE(wrapper != nullptr); + + ManagedObjectWrapper::Destroy(wrapper); + } + + HRESULT CreateWrapperForExternal( + _In_ IUnknown* external, + _In_ INT32 flagsRaw, + _In_ size_t contextSize, + _Outptr_ void** context) noexcept + { + _ASSERTE(external != nullptr && context != nullptr); + + HRESULT hr; + + // Convert input to appropriate type. + auto flags = static_cast(flagsRaw); + + NativeObjectWrapperContext* wrapperContext; + RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); + + *context = wrapperContext->GetRuntimeContext(); + return S_OK; + } + + void DestroyWrapperForExternal(_In_ void* contextMaybe) noexcept + { + NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); + + // A caller should not be destroying a context without knowing if the context is valid. + _ASSERTE(context != nullptr); + + // Check if the tracker object manager should be informed prior to being destroyed. + if (context->GetReferenceTrackerFast() != nullptr) + { + // We only call this during a GC so ignore the failure as + // there is no way we can handle that failure at this point. + HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(context); + _ASSERTE(SUCCEEDED(hr)); + (void)hr; + } + + NativeObjectWrapperContext::Destroy(context); + } + + bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept + { + return TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(objectHandle); + } + + void GetIUnknownImpl( + _Out_ void** fpQueryInterface, + _Out_ void** fpAddRef, + _Out_ void** fpRelease) noexcept + { + ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); + } + + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_opt_ OBJECTHANDLE handle) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); + if (wrapper == nullptr) + return E_INVALIDARG; + + ULONG count = wrapper->AddRef(); + if (count == 1) + { + // No object was supplied so reactivation isn't possible. + if (handle == nullptr) + { + wrapper->Release(); + return E_HANDLE; + } + + ::InterlockedExchangePointer(&wrapper->Target, handle); + return S_FALSE; + } + + return S_OK; + } + + HRESULT BeginExternalObjectReferenceTracking(_In_ RuntimeCallContext* cxt) noexcept + { + return TrackerObjectManager::BeginReferenceTracking(cxt); + } + + HRESULT EndExternalObjectReferenceTracking() noexcept + { + return TrackerObjectManager::EndReferenceTracking(); + } + } +} diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index e971a767c4ec82..5d03e6c6df947b 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -15,6 +15,7 @@ #ifdef _WIN32 #include +#include // COM interfaces // Common macro for working in COM #define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return hr; } } diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index b1dd437fc68c91..382b3a92c0eb6b 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -9,6 +9,7 @@ #include using OBJECTHANDLE = InteropLib::OBJECTHANDLE; +using RuntimeCallContext = InteropLibImports::RuntimeCallContext; namespace { @@ -83,11 +84,13 @@ namespace { } + // InteropLibImports::TriggerGC() return E_NOTIMPL; // [TODO] } STDMETHODIMP HostServices::ReleaseDisconnectedReferenceSources() { + // InteropLibImports::WaitForFinalizer() return E_NOTIMPL; // [TODO] } @@ -97,6 +100,9 @@ namespace // STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread() { + // InteropLibImports::ReleaseObjectsInContext() + // This probably should be done via a callback back into managed and + // let the object creator handle the neutering of the object. return E_NOTIMPL; // [TODO] } @@ -136,13 +142,13 @@ namespace ComHolder identity; RETURN_IF_FAILED(obj->QueryInterface(&identity)); - // Get or create an existing implementation for the this external. + // Get or create an existing implementation for this external. ComHolder target; - RETURN_IF_FAILED(GetOrCreateTrackerTargetForExternal( + RETURN_IF_FAILED(InteropLibImports::GetOrCreateTrackerTargetForExternal( impl, identity, - CreateRCWFlags::TrackerObject, - CreateCCWFlags::TrackerSupport, + (INT32)CreateObjectFlags::TrackerObject, + (INT32)CreateComInterfaceFlags::TrackerSupport, (void**)&target)); return target->QueryInterface(IID_IReferenceTrackerTarget, (void**)ppNewReference); @@ -157,67 +163,49 @@ namespace { return InteropLibImports::RemoveMemoryPressureForExternal(bytesAllocated); } -} -namespace -{ - // [TODO] - Volatile s_TrackerManager; // The one and only Tracker Manager instance - Volatile s_IsGCStarted = FALSE; + VolatilePtr s_TrackerManager; // The one and only Tracker Manager instance + Volatile s_HasTrackingStarted = FALSE; - // - // Tells GC whether walking all the Jupiter RCW is necessary, which only should happen - // if we have seen jupiter RCWs - // [TODO] - BOOL NeedToWalkRCWs() + // Indicates if walking the external objects is needed. + // (i.e. Have any IReferenceTracker instances been found?) + bool ShouldWalkExternalObjects() { - return (s_TrackerManager.load() != nullptr); + return (s_TrackerManager != nullptr); } - // // Callback implementation of IFindReferenceTargetsCallback - // [TODO] class FindDependentWrappersCallback : public IFindReferenceTargetsCallback { + NativeObjectWrapperContext* _nowCxt; + RuntimeCallContext* _runtimeCallCxt; + public: - FindDependentWrappersCallback(_In_ RCWInstance* rcw, _In_ TrackerRCWEnum* rcwEnum) - : _rcw{ rcw } - , _rcwEnum{ rcwEnum } + FindDependentWrappersCallback(_In_ NativeObjectWrapperContext* nowCxt, _In_ RuntimeCallContext* runtimeCallCxt) + : _nowCxt{ nowCxt } + , _runtimeCallCxt{ runtimeCallCxt } { + _ASSERTE(_nowCxt != nullptr && runtimeCallCxt != nullptr); } STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) { - assert(target != nullptr); - HRESULT hr; - CCW* ccw = CCW::MapIUnknownToWrapper(target); + if (target != nullptr) + return E_POINTER; - // Not a target we implemented. - if (ccw == nullptr) - return S_OK; + ManagedObjectWrapper* mow = ManagedObjectWrapper::MapFromIUnknown(target); - // - // Skip dependent handle creation if RCW/CCW points to the same managed object - // - if (_rcw->GetObjectGCHandle() == ccw->GetObjectGCHandle()) - return S_OK; - - // - // Jupiter might return CCWs with outstanding references that are either : - // 1. Neutered - in this case it is unsafe to touch m_ppThis - // 2. RefCounted handle NULLed out by GC - // - // Skip those to avoid crashes - // - if (!ccw->IsAlive()) + // Not a target we implemented. + if (mow == nullptr) return S_OK; - // - // Add a reference from rcw -> ccw so that GC knows about this reference - // - RETURN_IF_FAILED(_rcwEnum->AddReferenceFromRCWToCCW(_rcw, ccw)); + // Notify the runtime a reference path was found. + RETURN_IF_FAILED(InteropLibImports::FoundReferencePath( + _runtimeCallCxt, + _nowCxt->GetRuntimeContext(), + mow->Target)); return S_OK; } @@ -250,125 +238,59 @@ namespace (void)AddRef(); return S_OK; } - - private: - RCWInstance* _rcw; - TrackerRCWEnum* _rcwEnum; }; - // - // Ask Jupiter all the CCWs referenced (through native code) by this RCW and build reference for RCW -> CCW - // so that GC knows about this reference - // [TODO] - HRESULT WalkOneRCW(_In_ RCWInstance* rcw, _In_ TrackerRCWEnum* rcwEnum) + HRESULT OnExternalTrackerObject(_In_ NativeObjectWrapperContext* nowc, _In_ RuntimeCallContext* cxt) { - assert(rcw != nullptr && rcwEnum != nullptr); + _ASSERTE(nowc != nullptr && cxt != nullptr); HRESULT hr; - // Get IReferenceTracker * from RCW - we can call IReferenceTracker* from any thread and it won't be a proxy + // Get IReferenceTracker * from wrapper - we can call IReferenceTracker from any thread and it won't be a proxy ComHolder obj; - RETURN_IF_FAILED(rcw->GetInstanceProxy(&obj)); + RETURN_IF_FAILED(nowc->GetInstanceProxy(&obj)); - FindDependentWrappersCallback cb{ rcw, rcwEnum }; + // Ask the tracker instance to find all reference targets. + FindDependentWrappersCallback cb{ nowc, cxt }; RETURN_IF_FAILED(obj->FindTrackerTargets(&cb)); return S_OK; } -} - -void TrackerRCWManager::OnGCStartedWorker() -{ - // Due to the nesting GCStart/GCEnd pairs (see comment for this function), we need to check - // those flags inside nCondemnedGeneration >= 2 check - assert(s_IsGCStarted == FALSE); - assert(s_IsGlobalPeggingOn == TRUE); - - s_IsGCStarted = TRUE; - // - // Let Jupiter know we are about to walk RCWs so that they can lock their reference cache - // Note that Jupiter doesn't need to unpeg all CCWs at this point and they can do the pegging/unpegging in FindTrackerTargetsCompleted - // - assert(s_TrackerManager.load() != nullptr); - s_TrackerManager.load()->ReferenceTrackingStarted(); + // [TODO] + HRESULT WalkExternalTrackerObjects(_In_ RuntimeCallContext* cxt) + { + BOOL walkFailed = FALSE; + HRESULT hr; - // From this point, jupiter decides whether a CCW should be pegged or not as global pegging flag is now off - s_IsGlobalPeggingOn = FALSE; + void* extObjContext = nullptr; + while (S_OK == (hr = InteropLibImports::IteratorNext(cxt, &extObjContext))) + { + _ASSERTE(extObjContext != nullptr); - // - // OK. Time to walk all the reference RCWs - // - WalkRCWs(); -} + NativeObjectWrapperContext* nowc = NativeObjectWrapperContext::MapFromRuntimeContext(extObjContext); -void TrackerRCWManager::OnGCFinishedWorker() -{ - // - // Let Jupiter know RCW walk is done and they need to: - // 1. Unpeg all CCWs if the CCW needs to be unpegged (when the CCW is only reachable by other jupiter RCWs) - // 2. Peg all CCWs if the CCW needs to be pegged (when the above condition is not true) - // 3. Unlock reference cache when they are done - // - // If the walk has failed - Jupiter doesn't need to do anything and could just return immediately - // - assert(s_TrackerManager.load() != nullptr); - s_TrackerManager.load()->ReferenceTrackingCompleted(); + // Check if the object is a tracker object. + if (nowc->GetReferenceTrackerFast() == nullptr) + continue; - s_IsGlobalPeggingOn = TRUE; - s_IsGCStarted = FALSE; -} + hr = OnExternalTrackerObject(nowc, cxt); + if (FAILED(hr)) + break; + } -// -// Walk all the jupiter RCWs in all AppDomains and build references from RCW -> CCW as we go -// -void TrackerRCWManager::WalkRCWs() -{ - BOOL walkFailed = FALSE; - HRESULT hr; + if (FAILED(hr)) + { + // Remember the fact that we've failed and stop walking + walkFailed = TRUE; + InteropLibImports::SetGlobalPeggingState(true); + } - try - { - TrackerRCWEnum* rcwEnum = TrackerRCWEnum::GetInstance(); - assert(rcwEnum != nullptr); - - // - // Reset the cache - // - rcwEnum->ResetDependentHandles(); - - // - // Enumerate Jupiter RCWs - // - hr = rcwEnum->EnumerateJupiterRCWs(WalkOneRCW); - - // - // Shrink the dependent handle cache if necessary and clear unused handles. - // - rcwEnum->ShrinkDependentHandles(); - } - catch (...) - { - hr = E_FAIL; - } + _ASSERTE(s_TrackerManager != nullptr); + (void)s_TrackerManager->FindTrackerTargetsCompleted(walkFailed); - if (FAILED(hr)) - { - // Remember the fact that we've failed and stop walking - walkFailed = TRUE; - s_IsGlobalPeggingOn = TRUE; + return hr; } - - // - // Let Jupiter know RCW walk is done and they need to: - // 1. Unpeg all CCWs if the CCW needs to be unpegged (when the CCW is only reachable by other jupiter RCWs) - // 2. Peg all CCWs if the CCW needs to be pegged (when the above condition is not true) - // 3. Unlock reference cache when they are done - // - // If the walk has failed - Jupiter doesn't need to do anything and could just return immediately - // - assert(s_TrackerManager.load() != nullptr); - s_TrackerManager.load()->FindTrackerTargetsCompleted(walkFailed); } bool TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current) @@ -382,7 +304,7 @@ bool TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(_In_ OBJECTHAND HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* obj) { _ASSERTE(obj != nullptr); - if (s_TrackerManager.load() != nullptr) + if (s_TrackerManager != nullptr) return S_OK; // Retrieve IReferenceTrackerManager @@ -390,23 +312,14 @@ HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* o ComHolder trackerManager; RETURN_IF_FAILED(obj->GetReferenceTrackerManager(&trackerManager)); - ComHolder clrServices; - RETURN_IF_FAILED(g_HostServicesInstance.QueryInterface(IID_IReferenceTrackerHost, (void**)&clrServices)); - - // [TODO] Temporarily switch back to coop and disable GC to avoid racing with the very first RCW walk - //GCX_COOP(); - //GCX_FORBID(); + ComHolder hostServices; + RETURN_IF_FAILED(g_HostServicesInstance.QueryInterface(IID_IReferenceTrackerHost, (void**)&hostServices)); - IReferenceTrackerManager* expected = nullptr; - if (s_TrackerManager.compare_exchange_strong(expected, trackerManager.p)) + // Attempt to set the tracker instance. + if (::InterlockedCompareExchangePointer((void**)&s_TrackerManager, trackerManager.p, nullptr) == nullptr) { (void)trackerManager.Detach(); // Ownership has been transfered - - // - // OK. It is time to do our initialization - // It's safe to do it here because we are in COOP and only one thread wins the race - // - RETURN_IF_FAILED(s_TrackerManager.load()->SetReferenceTrackerHost(clrServices)); + RETURN_IF_FAILED(s_TrackerManager->SetReferenceTrackerHost(hostServices)); } return S_OK; @@ -482,68 +395,71 @@ namespace } __except (RCWWalker_UnhandledExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { - assert(false && "Should not get here"); + _ASSERTE(false && "Should not get here"); } } } -// -// Note that we could get nested GCStart/GCEnd calls, such as : -// GCStart for Gen 2 background GC -// GCStart for Gen 0/1 foregorund GC -// GCEnd for Gen 0/1 foreground GC -// .... -// GCEnd for Gen 2 background GC -// -// The nCondemnedGeneration >= 2 check takes care of this nesting problem -// -void TrackerObjectManager::OnGCStarted(_In_ int nCondemnedGeneration) +HRESULT TrackerObjectManager::BeginReferenceTracking(_In_ RuntimeCallContext* cxt) { - if (nCondemnedGeneration < 2) // We are only doing walk in Gen2 GC - return; + _ASSERTE(cxt != nullptr); - if (!NeedToWalkRCWs()) // Have we seen Jupiter RCWs? - return; + if (!ShouldWalkExternalObjects()) + return S_FALSE; - // Make sure we fail fast if anything goes wrong when we interact with Jupiter - SetupFailFastFilterAndCall(TrackerObjectManager::OnGCStartedWorker); + HRESULT hr; + + _ASSERTE(s_HasTrackingStarted == FALSE); + _ASSERTE(InteropLibImports::GetGlobalPeggingState()); + + s_HasTrackingStarted = TRUE; + + // From this point, the tracker runtime decides whether a target + // should be pegged or not as the global pegging flag is now off. + InteropLibImports::SetGlobalPeggingState(false); + + // Let the tracker runtime know we are about to walk external objects so that + // they can lock their reference cache. Note that the tracker runtime doesn't need to + // unpeg all external objects at this point and they can do the pegging/unpegging. + // in FindTrackerTargetsCompleted. + _ASSERTE(s_TrackerManager != nullptr); + RETURN_IF_FAILED(s_TrackerManager->ReferenceTrackingStarted()); + + // Time to walk the external objects + RETURN_IF_FAILED(WalkExternalTrackerObjects(cxt)); + + return S_OK; } -// -// Note that we could get nested GCStart/GCEnd calls, such as : -// GCStart for Gen 2 background GC -// GCStart for Gen 0/1 foregorund GC -// GCEnd for Gen 0/1 foreground GC -// .... -// GCEnd for Gen 2 background GC -// -// The nCondemnedGeneration >= 2 check takes care of this nesting problem -// -void TrackerObjectManager::OnGCFinished(_In_ int nCondemnedGeneration) +HRESULT TrackerObjectManager::EndReferenceTracking() { - // - // Note that we need to check in both OnGCFinished and OnGCStarted - // As there could be multiple OnGCFinished with nCondemnedGeneration < 2 in the case of Gen 2 GC - // - // Also, if this is background GC, the NeedToWalkRCWs predicate may change from FALSE to TRUE while - // the GC is running. We don't want to do any work if it's the case (i.e. if s_IsGCStarted is FALSE). - // - if (nCondemnedGeneration >= 2 // We are only doing walk in Gen2 GC - && NeedToWalkRCWs() // Have we seen Jupiter RCWs? - && s_IsGCStarted == TRUE) // Had we seen Jupiter RCWs when the GC started? - { - // Make sure we fail fast if anything goes wrong when we interact with Jupiter - SetupFailFastFilterAndCall(TrackerObjectManager::OnGCFinishedWorker); - } + if (s_HasTrackingStarted != TRUE + || !ShouldWalkExternalObjects()) + return S_FALSE; + + HRESULT hr; + + // Let the tracker runtime know the external object walk is done and they need to: + // 1. Unpeg all managed object wrappers (mow) if the (mow) needs to be unpegged + // (i.e. when the (mow) is only reachable by other external tracker objects). + // 2. Peg all mows if the mow needs to be pegged (i.e. when the above condition is not true) + // 3. Unlock reference cache when they are done. + _ASSERTE(s_TrackerManager != nullptr); + hr = s_TrackerManager->ReferenceTrackingCompleted(); + _ASSERTE(SUCCEEDED(hr)); + + InteropLibImports::SetGlobalPeggingState(true); + s_HasTrackingStarted = FALSE; + + return hr; } void TrackerObjectManager::OnShutdown() { - IReferenceTrackerManager* trackerManager = s_TrackerManager.exchange(nullptr); - if (trackerManager != nullptr) + IReferenceTrackerManager* trackerManager = s_TrackerManager; + if (::InterlockedCompareExchangePointer((void**)&s_TrackerManager, nullptr, trackerManager) == trackerManager) { - // Make sure s_TrackerManager is always either null or a valid IReferenceTrackerManager * - // this will make crash easier to diagnose + // Make sure s_TrackerManager is either null or a valid pointer. trackerManager->Release(); } } diff --git a/src/coreclr/src/vm/ceemain.cpp b/src/coreclr/src/vm/ceemain.cpp index 2790349ee93836..23b31b6e07e1dc 100644 --- a/src/coreclr/src/vm/ceemain.cpp +++ b/src/coreclr/src/vm/ceemain.cpp @@ -186,7 +186,7 @@ #include "runtimecallablewrapper.h" #include "notifyexternals.h" #include "mngstdinterfaces.h" -#include "rcwwalker.h" +#include "interoplibinterface.h" #endif // FEATURE_COMINTEROP #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 03512385e093fd..c4fc47ffdf36a5 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -141,7 +141,7 @@ namespace }; private: - friend class InteropLibImports::RuntimeCallContext; + friend struct InteropLibImports::RuntimeCallContext; SHash _hashMap; Crst _lock; @@ -537,7 +537,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -556,7 +556,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -589,6 +589,31 @@ namespace InteropLibImports DestroyHandleCommon(objectHandle, InstanceHandleType); } + bool GetGlobalPeggingState() noexcept + { + CONTRACTL + { + NOTHROW; + MODE_ANY; + } + CONTRACTL_END; + + return (RCWWalker::s_bIsGlobalPeggingOn != FALSE); + } + + void SetGlobalPeggingState(_In_ bool state) noexcept + { + CONTRACTL + { + NOTHROW; + MODE_ANY; + } + CONTRACTL_END; + + BOOL newState = state ? TRUE : FALSE; + VolatileStore(&RCWWalker::s_bIsGlobalPeggingOn, newState); + } + HRESULT GetOrCreateTrackerTargetForExternal( _In_ InteropLib::OBJECTHANDLE impl, _In_ IUnknown* externalComObject, @@ -613,6 +638,7 @@ namespace InteropLibImports // Switch to Cooperative mode since object references // are being manipulated. + BEGIN_EXTERNAL_ENTRYPOINT(&hr) { GCX_COOP(); @@ -626,31 +652,27 @@ namespace InteropLibImports gc.implRef = ObjectFromHandle(implHandle); - EX_TRY - { - // Get wrapper for external object - gc.objRef = GetOrCreateObjectForComInstanceInternal( - gc.implRef, - externalComObject, - externalObjectFlags); - - // Get wrapper for managed object - *trackerTarget = GetOrCreateComInterfaceForObjectInternal( - gc.implRef, - gc.objRef, - trackerTargetFlags); - } - EX_CATCH_HRESULT(hr); + // Get wrapper for external object + gc.objRef = GetOrCreateObjectForComInstanceInternal( + gc.implRef, + externalComObject, + externalObjectFlags); + + // Get wrapper for managed object + *trackerTarget = GetOrCreateComInterfaceForObjectInternal( + gc.implRef, + gc.objRef, + trackerTargetFlags); GCPROTECT_END(); } + END_EXTERNAL_ENTRYPOINT; return hr; } - class RuntimeCallContext + struct RuntimeCallContext { - public: RuntimeCallContext(_In_ ExtObjCxtCache* cache, _In_ ExtObjCxtRefCache* refCache) : Curr{ cache->_hashMap.Begin() } , End{ cache->_hashMap.End() } @@ -922,6 +944,35 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) // RCWWalker::OnGCStarted(nCondemnedGeneration); + // + // Note that we could get nested GCStart/GCEnd calls, such as : + // GCStart for Gen 2 background GC + // GCStart for Gen 0/1 foregorund GC + // GCEnd for Gen 0/1 foreground GC + // .... + // GCEnd for Gen 2 background GC + // + // The nCondemnedGeneration >= 2 check takes care of this nesting problem + // + // See Interop::OnGCFinished() + if (nCondemnedGeneration >= 2) + { + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + _ASSERTE(cache != NULL); + + RCWRefCache *refCache = GetAppDomain()->GetRCWRefCache(); + + // Reset the ref cache + refCache->ResetDependentHandles(); + + // Create a call context for the InteropLib. + InteropLibImports::RuntimeCallContext cxt(cache, refCache); + (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt); + + // Shrink cache and clear unused handles. + refCache->ShrinkDependentHandles(); + } + #endif // FEATURE_COMINTEROP } @@ -940,5 +991,37 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration) // RCWWalker::OnGCFinished(nCondemnedGeneration); + // + // Note that we could get nested GCStart/GCEnd calls, such as : + // GCStart for Gen 2 background GC + // GCStart for Gen 0/1 foregorund GC + // GCEnd for Gen 0/1 foreground GC + // .... + // GCEnd for Gen 2 background GC + // + // The nCondemnedGeneration >= 2 check takes care of this nesting problem + // + // See Interop::OnGCStarted() + if (nCondemnedGeneration >= 2) + (void)InteropLib::Com::EndExternalObjectReferenceTracking(); + +#endif // FEATURE_COMINTEROP +} + +void Interop::OnRuntimeShutdown() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + // Release IJupiterGCMgr* + RCWWalker::OnEEShutdown(); + + InteropLib::Shutdown(); + #endif // FEATURE_COMINTEROP } diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 70ffe6a17774f5..2d7c1804f9788a 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -47,4 +47,7 @@ class Interop // Notify when GC finished static void OnGCFinished(_In_ int nCondemnedGeneration); + + // Notify when the runtime is shutting down. + static void OnRuntimeShutdown(); }; From 1bfb390d9fd8f28885f2a228428f61bbd0cf9025 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 5 Feb 2020 19:36:03 -0800 Subject: [PATCH 23/83] Add new API to make object release requests. --- .../Runtime/InteropServices/ComWrappers.cs | 17 +++++++++++++++++ src/coreclr/src/vm/metasig.h | 1 + src/coreclr/src/vm/mscorlib.h | 1 + .../ref/System.Runtime.InteropServices.cs | 1 + 4 files changed, 20 insertions(+) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 2352151345661c..4ae12a3ca248dd 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; @@ -166,6 +167,22 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb internal static object CallCreateObject(ComWrappers comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags) => comWrappersImpl.CreateObject(externalComObject, flags); + /// + /// Called when a request is made for a collection of objects to be released. + /// + /// Collection of objects to release. + /// + /// The default implementation of this function throws . + /// + protected virtual void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + // Call to execute the virtual instance function + internal static void CallReleaseObjects(ComWrappers comWrappersImpl, IEnumerable objects) + => comWrappersImpl.ReleaseObjects(objects); + /// /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. /// diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 27c1a2fa87666b..e7830468cfa589 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -198,6 +198,7 @@ DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j)) +DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) DEFINE_METASIG(SM(Int_Int_RetVoid, i i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 6c67e2dca36f5b..12ec6d8929123a 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -464,6 +464,7 @@ DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFl DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_RetObj) +DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid) #endif //FEATURE_COMINTEROP DEFINE_CLASS(SERIALIZATION_INFO, Serialization, SerializationInfo) diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index a5af9b45999ee2..7980c93c7e45d1 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1014,6 +1014,7 @@ public struct ComInterfaceDispatch protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags); + protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterForReferenceTrackerHost() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } } From 3792204a170bc527d5f656bdfeea29ab94e76e36 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 5 Feb 2020 19:37:56 -0800 Subject: [PATCH 24/83] Address object release issues. --- src/coreclr/src/interop/comwrappers.cpp | 17 ++--- src/coreclr/src/interop/comwrappers.h | 14 +--- src/coreclr/src/interop/inc/interoplib.h | 3 +- .../src/interop/inc/interoplibimports.h | 3 + src/coreclr/src/interop/interoplib.cpp | 13 ++-- .../src/interop/trackerobjectmanager.cpp | 26 +++----- src/coreclr/src/vm/interoplibinterface.cpp | 65 +++++++++++++++++-- 7 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 796162cbbd3e2d..4475b4e46cf9c3 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -576,9 +576,6 @@ HRESULT NativeObjectWrapperContext::Create( RETURN_IF_FAILED(TrackerObjectManager::OnIReferenceTrackerFound(trackerObject)); } - ComHolder reference; - RETURN_IF_FAILED(CreateAgileReference(external, &reference)); - // Allocate memory for the RCW char* cxtMem = (char*)InteropLibImports::MemAlloc(sizeof(NativeObjectWrapperContext) + runtimeContextSize, AllocScenario::NativeObjectWrapper); if (cxtMem == nullptr) @@ -589,9 +586,9 @@ HRESULT NativeObjectWrapperContext::Create( // Contract specifically requires zeroing out runtime context. std::memset(runtimeContext, 0, runtimeContextSize); - NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ trackerObject, reference, runtimeContext }; + NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ trackerObject, runtimeContext }; - if (contextLocal->GetReferenceTrackerFast() != nullptr) + if (contextLocal->GetReferenceTracker() != nullptr) { // Inform the tracker object manager _ASSERTE((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject); @@ -617,21 +614,19 @@ void NativeObjectWrapperContext::Destroy(_In_ NativeObjectWrapperContext* wrappe InteropLibImports::MemFree(wrapper, AllocScenario::NativeObjectWrapper); } -NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference, _In_ void* runtimeContext) +NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ void* runtimeContext) : _trackerObject{ trackerObject } - , _objectReference{ reference } , _runtimeContext{ runtimeContext } #ifdef _DEBUG , _sentinal{ ContextSentinal } #endif { - (void)_objectReference->AddRef(); + (void)_trackerObject->AddRef(); } NativeObjectWrapperContext::~NativeObjectWrapperContext() { - _trackerObject = nullptr; - (void)_objectReference->Release(); + (void)_trackerObject->Release(); } void* NativeObjectWrapperContext::GetRuntimeContext() const @@ -639,7 +634,7 @@ void* NativeObjectWrapperContext::GetRuntimeContext() const return _runtimeContext; } -IReferenceTracker* NativeObjectWrapperContext::GetReferenceTrackerFast() const +IReferenceTracker* NativeObjectWrapperContext::GetReferenceTracker() const { return _trackerObject; } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 64618b0e7c50e6..0f52bdffac9ed4 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -121,7 +121,6 @@ class NativeObjectWrapperContext #endif IReferenceTracker* _trackerObject; - IAgileReference* _objectReference; void* _runtimeContext; public: // static @@ -139,22 +138,15 @@ class NativeObjectWrapperContext static void Destroy(_In_ NativeObjectWrapperContext* wrapper); private: - NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ IAgileReference* reference, _In_ void* runtimeContext); + NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ void* runtimeContext); ~NativeObjectWrapperContext(); public: // Get the associated runtime context for this context. void* GetRuntimeContext() const; - // Get the IReferenceTracker instance without going through the reference proxy. - IReferenceTracker* GetReferenceTrackerFast() const; - - // Get a type instance of the desired type through the reference proxy. - template - HRESULT GetInstanceProxy(_Outptr_ T** t) - { - return _objectReference->Resolve(t); - } + // Get the IReferenceTracker instance. + IReferenceTracker* GetReferenceTracker() const; }; // API for creating an IAgileReference instance to a supplied IUnknown instance. diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 2c75f2cad9751d..923dd9d67e55aa 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -44,7 +44,8 @@ namespace InteropLib _In_ IUnknown* external, _In_ INT32 flags, _In_ size_t contextSize, - _Outptr_ void** context) noexcept; + _Outptr_ void** context, + _Outptr_ IUnknown** agileReference) noexcept; // Destroy the supplied wrapper. void DestroyWrapperForExternal(_In_ void* context) noexcept; diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 7c82376d8f9195..659c5fb174c0d2 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -29,6 +29,9 @@ namespace InteropLibImports // [TODO] HRESULT RequestGarbageCollectionForExternal() noexcept; + // Release objects associated with the current thread. + HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept; + // Delete Object instance handle void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index fe5129e48cb611..f6609756c88e68 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -60,10 +60,11 @@ namespace InteropLib HRESULT CreateWrapperForExternal( _In_ IUnknown* external, _In_ INT32 flagsRaw, - _In_ size_t contextSize, - _Outptr_ void** context) noexcept + _In_ size_t contextSize, + _Outptr_ void** context, + _Outptr_ IUnknown** agileReference) noexcept { - _ASSERTE(external != nullptr && context != nullptr); + _ASSERTE(external != nullptr && context != nullptr && agileReference != nullptr); HRESULT hr; @@ -73,6 +74,10 @@ namespace InteropLib NativeObjectWrapperContext* wrapperContext; RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); + ComHolder reference; + RETURN_IF_FAILED(CreateAgileReference(external, &reference)); + *agileReference = reference.Detach(); + *context = wrapperContext->GetRuntimeContext(); return S_OK; } @@ -85,7 +90,7 @@ namespace InteropLib _ASSERTE(context != nullptr); // Check if the tracker object manager should be informed prior to being destroyed. - if (context->GetReferenceTrackerFast() != nullptr) + if (context->GetReferenceTracker() != nullptr) { // We only call this during a GC so ignore the failure as // there is no way we can handle that failure at this point. diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 382b3a92c0eb6b..e54c0a79e313a8 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -94,16 +94,13 @@ namespace return E_NOTIMPL; // [TODO] } - // - // Release context-bound RCWs and Jupiter RCWs (which are free-threaded but context-bound) - // in the current apartment - // STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread() { - // InteropLibImports::ReleaseObjectsInContext() - // This probably should be done via a callback back into managed and - // let the object creator handle the neutering of the object. - return E_NOTIMPL; // [TODO] + OBJECTHANDLE impl = HostServices::RuntimeImpl; + if (impl == nullptr) + return E_NOT_SET; + + return InteropLibImports::ReleaseExternalObjectsFromCurrentThread(impl); } // @@ -245,10 +242,7 @@ namespace _ASSERTE(nowc != nullptr && cxt != nullptr); HRESULT hr; - - // Get IReferenceTracker * from wrapper - we can call IReferenceTracker from any thread and it won't be a proxy - ComHolder obj; - RETURN_IF_FAILED(nowc->GetInstanceProxy(&obj)); + IReferenceTracker* obj = nowc->GetReferenceTracker(); // Ask the tracker instance to find all reference targets. FindDependentWrappersCallback cb{ nowc, cxt }; @@ -271,7 +265,7 @@ namespace NativeObjectWrapperContext* nowc = NativeObjectWrapperContext::MapFromRuntimeContext(extObjContext); // Check if the object is a tracker object. - if (nowc->GetReferenceTrackerFast() == nullptr) + if (nowc->GetReferenceTracker() == nullptr) continue; hr = OnExternalTrackerObject(nowc, cxt); @@ -330,7 +324,7 @@ HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ NativeObjectWrapperContex _ASSERTE(cxt != nullptr); HRESULT hr; - IReferenceTracker* obj = cxt->GetReferenceTrackerFast(); + IReferenceTracker* obj = cxt->GetReferenceTracker(); _ASSERTE(obj != nullptr); // Notify tracker runtime that we've created a new wrapper for this object. @@ -350,8 +344,8 @@ HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ NativeObjectWrapperCon _ASSERTE(cxt != nullptr); HRESULT hr; - ComHolder obj; - RETURN_IF_FAILED(cxt->GetInstanceProxy(&obj)); + IReferenceTracker* obj = cxt->GetReferenceTracker(); + _ASSERTE(obj != nullptr); // Notify tracker runtime that we are about to destroy a wrapper // (same timing as short weak handle) for this object. diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index c4fc47ffdf36a5..d1100749841ecb 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -308,6 +308,27 @@ namespace return retObjRef; } + void CallReleaseObjects( + _In_ OBJECTREF* implPROTECTED, + _In_ OBJECTREF* objsEnumPROTECTED) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(implPROTECTED != NULL); + PRECONDITION(objsEnumPROTECTED != NULL); + } + CONTRACTL_END; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__RELEASE_OBJECTS); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*objsEnumPROTECTED); + CALL_MANAGED_METHOD_NORET(args); + } + void* GetOrCreateComInterfaceForObjectInternal( _In_ OBJECTREF impl, _In_ OBJECTREF instance, @@ -461,14 +482,15 @@ namespace } else { - // Create context for the possibly new external COM object. + // Create context and IAgileReference instance for the possibly new external COM object. ExtObjCxtHolder newContext; - hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), (void**)&newContext); + SafeComHolder agileRef; + hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), (void**)&newContext, &agileRef); if (FAILED(hr)) COMPlusThrow(hr); // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, identity, flags); + gc.objRef = CallGetObject(&gc.implRef, agileRef, flags); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); @@ -570,9 +592,44 @@ namespace InteropLibImports return hr; } - void RequestGarbageCollection() noexcept + HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept { + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + ::OBJECTHANDLE implHandle = static_cast<::OBJECTHANDLE>(handle); + + HRESULT hr = S_OK; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + // Switch to cooperative mode so the cache can be queried. + GCX_COOP(); + + struct + { + OBJECTREF implRef; + OBJECTREF objsEnumRef; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + gc.implRef = ObjectFromHandle(implHandle); + + // + // [TODO] Pass the objects along to get released. + // + + CallReleaseObjects(&gc.implRef, &gc.objsEnumRef); + + GCPROTECT_END(); + } + END_EXTERNAL_ENTRYPOINT; + + return hr; } void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept From c83e6ecdae4e292570c825afd6a77e4ecdfb7df6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 6 Feb 2020 18:09:15 -0800 Subject: [PATCH 25/83] Update public API --- .../Runtime/InteropServices/ComWrappers.cs | 12 +- .../src/interop/inc/interoplibimports.h | 12 +- src/coreclr/src/interop/platform.h | 4 - .../src/interop/trackerobjectmanager.cpp | 53 +----- src/coreclr/src/vm/interoplibinterface.cpp | 177 +++++++++++++++--- src/coreclr/src/vm/metasig.h | 2 +- src/coreclr/src/vm/mscorlib.h | 2 +- .../ref/System.Runtime.InteropServices.cs | 2 +- 8 files changed, 176 insertions(+), 88 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 4ae12a3ca248dd..148f0978e11750 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -156,16 +156,20 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack retValue); /// - /// Create a managed object for respecting the values of . + /// Create a managed object for the object pointed at by respecting the values of . /// /// Object to import for usage into the .NET runtime. + /// IAgileReference pointing at the object to import into the .NET runtime. /// Flags used to describe the external object. /// Returns a managed object associated with the supplied external COM object. - protected abstract object CreateObject(IntPtr externalComObject, CreateObjectFlags flags); + /// + /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. + /// + protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object CallCreateObject(ComWrappers comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags) - => comWrappersImpl.CreateObject(externalComObject, flags); + internal static object CallCreateObject(ComWrappers comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) + => comWrappersImpl.CreateObject(externalComObject, agileObjectRef, flags); /// /// Called when a request is made for a collection of objects to be released. diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 659c5fb174c0d2..216c98b782c5b8 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -27,7 +27,17 @@ namespace InteropLibImports // Remove memory pressure from the runtime's GC calculations. HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept; - // [TODO] HRESULT RequestGarbageCollectionForExternal() noexcept; + enum class GcRequest + { + Default, + Blocking + }; + + // Request a GC from the runtime. + HRESULT RequestGarbageCollectionForExternal(_In_ GcRequest req) noexcept; + + // Wait for the runtime's finalizer to clean up objects. + HRESULT WaitForRuntimeFinalizerForExternal() noexcept; // Release objects associated with the current thread. HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept; diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index 5d03e6c6df947b..f3e08ba6f7c791 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -25,8 +25,4 @@ #define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") -// BEGIN [TODO] Remove -#include -// END [TODO] Remove - #endif // _INTEROP_PLATFORM_H_ \ No newline at end of file diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index e54c0a79e313a8..2c0c314b2a76d6 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -80,18 +80,18 @@ namespace STDMETHODIMP HostServices::DisconnectUnusedReferenceSources(_In_ DWORD flags) { + InteropLibImports::GcRequest type = InteropLibImports::GcRequest::Default; + + // Request an expensive blocking GC when a suspend is occurring. if (flags & XAML_REFERENCETRACKER_DISCONNECT_SUSPEND) - { - } + type = InteropLibImports::GcRequest::Blocking; - // InteropLibImports::TriggerGC() - return E_NOTIMPL; // [TODO] + return InteropLibImports::RequestGarbageCollectionForExternal(type); } STDMETHODIMP HostServices::ReleaseDisconnectedReferenceSources() { - // InteropLibImports::WaitForFinalizer() - return E_NOTIMPL; // [TODO] + return InteropLibImports::WaitForRuntimeFinalizerForExternal(); } STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread() @@ -251,9 +251,10 @@ namespace return S_OK; } - // [TODO] HRESULT WalkExternalTrackerObjects(_In_ RuntimeCallContext* cxt) { + _ASSERTE(cxt != nullptr); + BOOL walkFailed = FALSE; HRESULT hr; @@ -356,44 +357,6 @@ HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ NativeObjectWrapperCon return S_OK; } -namespace -{ - // - // We never expect exceptions to be thrown outside of RCWWalker - // So make sure we fail fast here, instead of going through normal - // exception processing and fail later - // This will make analyzing dumps much easier - // - LONG RCWWalker_UnhandledExceptionFilter(DWORD code, _In_ EXCEPTION_POINTERS* excep) - { - if ((excep->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) - || (excep->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)) - { - // We don't want to fail fast on debugger exceptions - return EXCEPTION_CONTINUE_SEARCH; - } - - assert(false); - std::abort(); - - return EXCEPTION_EXECUTE_HANDLER; - } - - using OnGCEventProc = void(*)(); - void SetupFailFastFilterAndCall(_In_ OnGCEventProc func) - { - __try - { - // Call the internal worker function which has the runtime contracts - func(); - } - __except (RCWWalker_UnhandledExceptionFilter(GetExceptionCode(), GetExceptionInformation())) - { - _ASSERTE(false && "Should not get here"); - } - } -} - HRESULT TrackerObjectManager::BeginReferenceTracking(_In_ RuntimeCallContext* cxt) { _ASSERTE(cxt != nullptr); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index d1100749841ecb..0e816bf28a823e 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -6,26 +6,14 @@ #include "common.h" #include "rcwrefcache.h" #include "rcwwalker.h" +#include "olecontexthelpers.h" +#include "finalizerthread.h" // Interop library header #include #include "interoplibinterface.h" -// void ____() -// { -// CONTRACTL -// { -// THROWS; -// //NOTHROW; -// MODE_COOPERATIVE; -// // MODE_PREEMPTIVE; -// // MODE_ANY; -// // PRECONDITION(pSrc != NULL); -// } -// CONTRACTL_END; -// } - namespace { // This class is used to track the external object within the runtime. @@ -35,9 +23,16 @@ namespace static DWORD CollectedFlag; void* Identity; + void* ThreadContext; DWORD SyncBlockIndex; DWORD IsCollected; + bool IsActive() const + { + return (IsCollected != CollectedFlag) + && (SyncBlockIndex != InvalidSyncBlockIndex); + } + void MarkCollected() { _ASSERTE(GCHeapUtilities::IsGCInProgress()); @@ -45,10 +40,18 @@ namespace IsCollected = CollectedFlag; } - bool IsActive() const + OBJECTREF GetObjectRef() { - return (IsCollected != CollectedFlag) - && (SyncBlockIndex != InvalidSyncBlockIndex); + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(IsActive()); + return ObjectToOBJECTREF(g_pSyncTable[SyncBlockIndex].m_Object); } }; @@ -157,6 +160,70 @@ namespace return (_lock.OwnedByCurrentThread() != FALSE); } + PTRARRAYREF CreateManagedEnumerable(_In_ void* threadContext) + { + CONTRACT(PTRARRAYREF) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(!IsLockHeld()); + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + + DWORD objCount; + DWORD objCountMax; + + struct + { + PTRARRAYREF arrRef; + PTRARRAYREF arrRefTmp; + } gc; + ::ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + { + LockHolder lock(this); + objCountMax = _hashMap.GetCount(); + } + + // Allocate the max number of objects needed. + gc.arrRef = (PTRARRAYREF)AllocateObjectArray(objCountMax, g_pObjectClass); + + // Populate the array + { + LockHolder lock(this); + Iterator curr = _hashMap.Begin(); + Iterator end = _hashMap.End(); + + ExternalObjectContext* inst; + for (objCount = 0; curr != end && objCount < objCountMax; objCount++, curr++) + { + inst = *curr; + if (inst->ThreadContext == threadContext) + gc.arrRef->SetAt(objCount, inst->GetObjectRef()); + } + } + + // Make the array the correct size + if (objCount < objCountMax) + { + gc.arrRefTmp = (PTRARRAYREF)AllocateObjectArray(objCount, g_pObjectClass); + + SIZE_T elementSize = gc.arrRef->GetComponentSize(); + + void *src = gc.arrRef->GetDataPtr(); + void *dest = gc.arrRefTmp->GetDataPtr(); + memcpyNoGCRefs(dest, src, objCount * elementSize); + gc.arrRef = gc.arrRefTmp; + } + + GCPROTECT_END(); + + RETURN gc.arrRef; + } + ExternalObjectContext* Find(_In_ IUnknown* instance) { CONTRACT(ExternalObjectContext*) @@ -284,6 +351,7 @@ namespace OBJECTREF CallGetObject( _In_ OBJECTREF* implPROTECTED, _In_ IUnknown* externalComObject, + _In_ IUnknown* agileObjectRef, _In_ INT32 flags) { CONTRACTL @@ -299,10 +367,11 @@ namespace OBJECTREF retObjRef; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 3); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(agileObjectRef); + args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags); CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); return retObjRef; @@ -473,12 +542,7 @@ namespace if (extObjCxt != NULL) { - if (!extObjCxt->IsActive()) - { - // [TODO] We are in a bad spot? - } - - gc.objRef = ObjectToOBJECTREF(g_pSyncTable[extObjCxt->SyncBlockIndex].m_Object); + gc.objRef = extObjCxt->GetObjectRef(); } else { @@ -490,12 +554,13 @@ namespace COMPlusThrow(hr); // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, agileRef, flags); + gc.objRef = CallGetObject(&gc.implRef, identity, agileRef, flags); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); // Update the new context with the object details. newContext->Identity = (void*)identity; + newContext->ThreadContext = GetCurrentCtxCookie(); newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); // Attempt to insert the new context into the cache. @@ -592,6 +657,53 @@ namespace InteropLibImports return hr; } + HRESULT RequestGarbageCollectionForExternal(_In_ GcRequest req) noexcept + { + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + if (req == GcRequest::Blocking) + { + GCHeapUtilities::GetGCHeap()->GarbageCollect(2, true, collection_blocking | collection_optimized); + } + else + { + _ASSERTE(req == GcRequest::Default); + GCHeapUtilities::GetGCHeap()->GarbageCollect(); + } + } + END_EXTERNAL_ENTRYPOINT; + + return hr; + } + + HRESULT WaitForRuntimeFinalizerForExternal() noexcept + { + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + FinalizerThread::FinalizerThreadWait(); + } + END_EXTERNAL_ENTRYPOINT; + + return hr; + } + HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept { CONTRACTL @@ -619,12 +731,16 @@ namespace InteropLibImports gc.implRef = ObjectFromHandle(implHandle); - // - // [TODO] Pass the objects along to get released. - // + // Pass the objects along to get released. + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + gc.objsEnumRef = cache->CreateManagedEnumerable(GetCurrentCtxCookie()); CallReleaseObjects(&gc.implRef, &gc.objsEnumRef); + // + // [TODO] Clear out objects' syncblocks. + // + GCPROTECT_END(); } END_EXTERNAL_ENTRYPOINT; @@ -793,8 +909,7 @@ namespace InteropLibImports // Get the external object's managed wrapper ExternalObjectContext* extObjContext = static_cast(extObjContextRaw); - _ASSERTE(extObjContext->IsActive()); - OBJECTREF source = ObjectToOBJECTREF(g_pSyncTable[extObjContext->SyncBlockIndex].m_Object); + OBJECTREF source = extObjContext->GetObjectRef(); // Get the target of the external object's reference. ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index e7830468cfa589..d754a95b6a996b 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -197,7 +197,7 @@ DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v)) DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) -DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j)) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS), j)) DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 12ec6d8929123a..510d9e05060afe 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -463,7 +463,7 @@ DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) -DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_RetObj) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj) DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid) #endif //FEATURE_COMINTEROP diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 7980c93c7e45d1..226c7ab8ea929a 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1013,7 +1013,7 @@ public struct ComInterfaceDispatch public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } - protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags); + protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterForReferenceTrackerHost() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } From d402fec6fe1180eff51d4c7960f19da9662acc86 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 7 Feb 2020 16:52:33 -0800 Subject: [PATCH 26/83] Add support for checking if an external object implements the IReferenceTracker interface from the runtime side. --- src/coreclr/src/interop/inc/interoplib.h | 3 + src/coreclr/src/interop/interoplib.cpp | 9 +++ src/coreclr/src/vm/interoplibinterface.cpp | 82 ++++++++++++++++++---- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 923dd9d67e55aa..e3ab21d583e553 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -50,6 +50,9 @@ namespace InteropLib // Destroy the supplied wrapper. void DestroyWrapperForExternal(_In_ void* context) noexcept; + // Check if the context is associated with a tracker runtime. + bool IsReferenceTrackerInstance(_In_ void* context) noexcept; + // Register a runtime implementation callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept; diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index f6609756c88e68..a68dca1b6c4376 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -102,6 +102,15 @@ namespace InteropLib NativeObjectWrapperContext::Destroy(context); } + bool IsReferenceTrackerInstance(_In_ void* contextMaybe) noexcept + { + NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); + if (context == nullptr) + return false; + + return (context->GetReferenceTracker() != nullptr); + } + bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept { return TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(objectHandle); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 0e816bf28a823e..66e8081fca7b26 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -19,17 +19,52 @@ namespace // This class is used to track the external object within the runtime. struct ExternalObjectContext { - static DWORD InvalidSyncBlockIndex; - static DWORD CollectedFlag; + static const DWORD InvalidSyncBlockIndex; void* Identity; void* ThreadContext; DWORD SyncBlockIndex; - DWORD IsCollected; + + enum + { + Flags_None = 0, + Flags_Collected = 1, + Flags_ReferenceTracker = 2, + }; + DWORD Flags; + + static void Construct( + _In_ ExternalObjectContext* cxt, + _In_ IUnknown* identity, + _In_opt_ void* threadContext, + _In_ DWORD syncBlockIndex, + _In_ DWORD flags) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(cxt != NULL); + PRECONDITION(threadContext != NULL); + PRECONDITION(syncBlockIndex != InvalidSyncBlockIndex); + } + CONTRACTL_END; + + cxt->Identity = (void*)identity; + cxt->ThreadContext = threadContext; + cxt->SyncBlockIndex = syncBlockIndex; + cxt->Flags = flags; + } + + bool IsSet(_In_ DWORD f) const + { + return ((Flags & f) == f); + } bool IsActive() const { - return (IsCollected != CollectedFlag) + return IsSet(Flags_Collected) && (SyncBlockIndex != InvalidSyncBlockIndex); } @@ -37,7 +72,7 @@ namespace { _ASSERTE(GCHeapUtilities::IsGCInProgress()); SyncBlockIndex = InvalidSyncBlockIndex; - IsCollected = CollectedFlag; + Flags |= Flags_Collected; } OBJECTREF GetObjectRef() @@ -55,8 +90,7 @@ namespace } }; - DWORD ExternalObjectContext::InvalidSyncBlockIndex = 0; // See syncblk.h - DWORD ExternalObjectContext::CollectedFlag = ~0; + const DWORD ExternalObjectContext::InvalidSyncBlockIndex = 0; // See syncblk.h static_assert((sizeof(ExternalObjectContext) % sizeof(void*)) == 0, "Keep context pointer size aligned"); @@ -160,9 +194,13 @@ namespace return (_lock.OwnedByCurrentThread() != FALSE); } - PTRARRAYREF CreateManagedEnumerable(_In_ void* threadContext) + // Create a managed IEnumerable instance for this collection. + // The collection should respect the supplied arguments. + // withFlags - If Flag_None, then ignore. Otherwise objects must have these flags. + // threadContext - The object must be associated with the supplied thread context. + OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext) { - CONTRACT(PTRARRAYREF) + CONTRACT(OBJECTREF) { THROWS; GC_TRIGGERS; @@ -201,8 +239,14 @@ namespace for (objCount = 0; curr != end && objCount < objCountMax; objCount++, curr++) { inst = *curr; - if (inst->ThreadContext == threadContext) + + // Only add objects that are in the correct thread + // context and have the appropriate flags set. + if (inst->ThreadContext == threadContext + && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet(withFlags))) + { gc.arrRef->SetAt(objCount, inst->GetObjectRef()); + } } } @@ -558,10 +602,16 @@ namespace if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); - // Update the new context with the object details. - newContext->Identity = (void*)identity; - newContext->ThreadContext = GetCurrentCtxCookie(); - newContext->SyncBlockIndex = gc.objRef->GetSyncBlockIndex(); + // Construct the new context with the object details. + DWORD flags = InteropLib::Com::IsReferenceTrackerInstance((void*)newContext) + ? ExternalObjectContext::Flags_ReferenceTracker + : ExternalObjectContext::Flags_None; + ExternalObjectContext::Construct( + newContext, + identity, + GetCurrentCtxCookie(), + gc.objRef->GetSyncBlockIndex(), + flags); // Attempt to insert the new context into the cache. { @@ -733,7 +783,9 @@ namespace InteropLibImports // Pass the objects along to get released. ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - gc.objsEnumRef = cache->CreateManagedEnumerable(GetCurrentCtxCookie()); + gc.objsEnumRef = cache->CreateManagedEnumerable( + ExternalObjectContext::Flags_ReferenceTracker, + GetCurrentCtxCookie()); CallReleaseObjects(&gc.implRef, &gc.objsEnumRef); From e508c34d5f7dc654fac7ba265e7148e7790c6aeb Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 7 Feb 2020 16:58:02 -0800 Subject: [PATCH 27/83] Add comment about IEnumerable optimization. --- src/coreclr/src/vm/interoplibinterface.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 66e8081fca7b26..fee5e0d9c1c9f2 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -198,6 +198,11 @@ namespace // The collection should respect the supplied arguments. // withFlags - If Flag_None, then ignore. Otherwise objects must have these flags. // threadContext - The object must be associated with the supplied thread context. + // + // [TODO] Performance improvement should be made here to provide a custom IEnumerable + // instead of a managed array. When the custom solution is done, the objects' syncblocks + // should be cleaned up of the internal IReferenceTracker instance stored within the + // context block held in the InteropLib side. OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext) { CONTRACT(OBJECTREF) @@ -789,10 +794,6 @@ namespace InteropLibImports CallReleaseObjects(&gc.implRef, &gc.objsEnumRef); - // - // [TODO] Clear out objects' syncblocks. - // - GCPROTECT_END(); } END_EXTERNAL_ENTRYPOINT; From 46c357c1b012e271d161fbcafbe61239d7e5caea Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 7 Feb 2020 19:16:25 -0800 Subject: [PATCH 28/83] Delay load ole32.dll to get RoGetAgileReference using GetProcAddress. --- src/coreclr/src/interop/agilereferences.cpp | 25 +++++++++++++++++++-- src/coreclr/src/vm/interoplibinterface.cpp | 4 ++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp index 9dbea37ed854bf..4702e8ff7591eb 100644 --- a/src/coreclr/src/interop/agilereferences.cpp +++ b/src/coreclr/src/interop/agilereferences.cpp @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// Runtime headers +#include + #include "comwrappers.h" #if (NTDDI_VERSION >= NTDDI_WINBLUE) @@ -24,12 +27,30 @@ WINOLEAPI RoGetAgileReference( #endif +namespace +{ + // Global function pointer for RoGetAgileReference + decltype(RoGetAgileReference)* fpRoGetAgileReference; +} + template<> HRESULT CreateAgileReference( _In_ IUnknown* object, _Outptr_ IAgileReference** agileReference) { - // [TODO] Fail gracefully on pre-Windows 8.1 plaforms. _ASSERTE(object != nullptr && agileReference != nullptr); - return ::RoGetAgileReference(AGILEREFERENCE_DEFAULT, __uuidof(object), object, agileReference); + + // If the pointer isn't set, then attempt to load it in process. + if (fpRoGetAgileReference == nullptr) + { + HMODULE hmod = WszLoadLibrary(W("ole32.dll")); + if (hmod != nullptr) + fpRoGetAgileReference = (decltype(RoGetAgileReference)*)::GetProcAddress(hmod, "RoGetAgileReference"); + + // Could't find binary or export. Either way, the OS version is too old. + if (fpRoGetAgileReference == nullptr) + return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); + } + + return fpRoGetAgileReference(AGILEREFERENCE_DEFAULT, __uuidof(object), object, agileReference); } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index fee5e0d9c1c9f2..0d9010af410efa 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -201,8 +201,8 @@ namespace // // [TODO] Performance improvement should be made here to provide a custom IEnumerable // instead of a managed array. When the custom solution is done, the objects' syncblocks - // should be cleaned up of the internal IReferenceTracker instance stored within the - // context block held in the InteropLib side. + // should be updated by removing the IReferenceTracker instance stored within the + // context block held on the InteropLib side. OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext) { CONTRACT(OBJECTREF) From 0d3b9cf70c840075a59a035f2f3efb2a8ad4837e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 7 Feb 2020 22:18:30 -0800 Subject: [PATCH 29/83] TEST - WIP --- src/coreclr/tests/src/Interop/CMakeLists.txt | 1 + .../COM/ComWrappers/ComWrappersTests.csproj | 3 + .../CMakeLists.txt | 10 + .../MockReferenceTrackerRuntime/Exports.def | 2 + .../ReferenceTrackerRuntime.cpp | 204 ++++++++++++++++++ .../ReferenceTrackerRuntime.h | 21 ++ 6 files changed, 241 insertions(+) create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h diff --git a/src/coreclr/tests/src/Interop/CMakeLists.txt b/src/coreclr/tests/src/Interop/CMakeLists.txt index 3e534c8f8c22f2..d8f23d3b8c0f02 100644 --- a/src/coreclr/tests/src/Interop/CMakeLists.txt +++ b/src/coreclr/tests/src/Interop/CMakeLists.txt @@ -81,6 +81,7 @@ if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(COM/NativeClients/Licensing) add_subdirectory(COM/NativeClients/DefaultInterfaces) add_subdirectory(COM/NativeClients/Dispatch) + add_subdirectory(COM/ComWrappers/MockReferenceTrackerRuntime) add_subdirectory(WinRT/NativeComponent) # IJW isn't supported on ARM64 diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj index e46ccfce4e3dd3..e82960ae1e1010 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj @@ -10,4 +10,7 @@ + + + diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt new file mode 100644 index 00000000000000..84222f3990b751 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt @@ -0,0 +1,10 @@ +project (MockReferenceTrackerRuntime) +include_directories( ${INC_PLATFORM_DIR} ) +set(SOURCES ReferenceTrackerRuntime.cpp Exports.def) + +# add the shared library +add_library (MockReferenceTrackerRuntime SHARED ${SOURCES}) +target_link_libraries(MockReferenceTrackerRuntime ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS MockReferenceTrackerRuntime DESTINATION bin) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def new file mode 100644 index 00000000000000..441edaa216ca54 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def @@ -0,0 +1,2 @@ +EXPORTS + CreateExternalObject PRIVATE \ No newline at end of file diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp new file mode 100644 index 00000000000000..7109705801ce47 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include +#include + +namespace +{ + class TrackerRuntimeManagerImpl : public IReferenceTrackerManager + { + CComPtr runtimeServices; + public: + STDMETHOD(ReferenceTrackingStarted)() + { + return E_NOTIMPL; + } + + STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) + { + return E_NOTIMPL; + } + + STDMETHOD(ReferenceTrackingCompleted)() + { + return E_NOTIMPL; + } + + STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost* pHostServices) + { + assert(pHostServices != nullptr); + return pHostServices->QueryInterface(&runtimeServices); + } + + // Lifetime maintained by stack - we don't care about ref counts + STDMETHOD_(ULONG, AddRef)() { return 1; } + STDMETHOD_(ULONG, Release)() { return 1; } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + if (ppvObject == nullptr) + return E_POINTER; + + if (IsEqualIID(riid, __uuidof(IReferenceTrackerManager))) + { + *ppvObject = static_cast(this); + } + else if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + }; + + TrackerRuntimeManagerImpl TrackerRuntimeManager; + + struct DECLSPEC_UUID("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09") ITest : public IUnknown + { + STDMETHOD(SetValue)(int i) = 0; + }; + + struct ExternalObject : public IExternalObject, public IReferenceTracker, public UnknownImpl + { + const size_t _id; + std::atomic _trackerCount; + bool _connected; + std::unordered_map> _elements; + + ExternalObject(size_t id) : _id{ id }, _trackerCount{0}, _connected{ false } + { } + + STDMETHOD(AddObjectRef)(_In_ IUnknown* c) + { + assert(c != nullptr); + + try + { + if (!_elements.insert(std::make_pair(size_t(c), CComPtr{ c })).second) + return S_FALSE; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + + CComPtr mowMaybe; + if (S_OK == c->QueryInterface(&mowMaybe)) + (void)mowMaybe->AddRefFromReferenceTracker(); + + return S_OK; + } + + STDMETHOD(DropObjectRef)(_In_ IUnknown* c) + { + assert(c != nullptr); + auto erased_count = _elements.erase((size_t)c); + if (erased_count > 0) + { + CComPtr mowMaybe; + if (S_OK == c->QueryInterface(&mowMaybe)) + { + for (decltype(erased_count) i = 0; i < erased_count; ++i) + (void)mowMaybe->ReleaseFromReferenceTracker(); + } + } + + return S_OK; + } + + STDMETHOD(UseObjectRefs)() + { + return E_NOTIMPL; + } + + STDMETHOD(ConnectFromTrackerSource)(); + STDMETHOD(DisconnectFromTrackerSource)(); + STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback* pCallback); + STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager** ppTrackerManager); + STDMETHOD(AddRefFromTrackerSource)(); + STDMETHOD(ReleaseFromTrackerSource)(); + STDMETHOD(PegFromTrackerSource)(); + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + return DoQueryInterface(riid, ppvObject, static_cast(this)); + } + + DEFINE_REF_COUNTING() + }; + + HRESULT STDMETHODCALLTYPE ExternalObject::ConnectFromTrackerSource() + { + _connected = true; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ExternalObject::DisconnectFromTrackerSource() + { + _connected = false; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ExternalObject::FindTrackerTargets(_In_ IFindReferenceTargetsCallback* pCallback) + { + assert(pCallback != nullptr); + + CComPtr mowMaybe; + for (auto &e : _elements) + { + if (S_OK == e.second->QueryInterface(&mowMaybe)) + { + (void)pCallback->FoundTrackerTarget(mowMaybe.p); + mowMaybe.Release(); + } + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ExternalObject::GetReferenceTrackerManager(_Outptr_ IReferenceTrackerManager** ppTrackerManager) + { + assert(ppTrackerManager != nullptr); + return TrackerRuntimeManager.QueryInterface(__uuidof(IReferenceTrackerManager), (void**)ppTrackerManager); + } + + HRESULT STDMETHODCALLTYPE ExternalObject::AddRefFromTrackerSource() + { + assert(0 <= _trackerCount); + ++_trackerCount; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ExternalObject::ReleaseFromTrackerSource() + { + assert(0 < _trackerCount); + --_trackerCount; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ExternalObject::PegFromTrackerSource() + { + /* Not used by runtime */ + return E_NOTIMPL; + } + + std::atomic CurrentObjectId{}; +} + +IExternalObject* STDMETHODCALLTYPE CreateExternalObject() +{ + return new ExternalObject{ CurrentObjectId++ }; +} diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h new file mode 100644 index 00000000000000..830c0df4ea9093 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +#ifndef _EXTERNAL_H_ +#define _EXTERNAL_H_ + +#include + +struct DECLSPEC_UUID("42951130-245C-485E-B60B-4ED4254256F8") IExternalObject : public IUnknown +{ + STDMETHOD(AddObjectRef)(_In_ IUnknown* c) = 0; + STDMETHOD(DropObjectRef)(_In_ IUnknown * c) = 0; + STDMETHOD(UseObjectRefs)() = 0; +}; + +// Create external object +EXPORT IExternalObject* STDMETHODCALLTYPE CreateExternalObject(); + +#endif // _EXTERNAL_H_ From 6e4169979d9fe988d5aa4a9a8afb64a9f10778b0 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 8 Feb 2020 11:32:09 -0800 Subject: [PATCH 30/83] Reduce uses of GetReferenceTracker() - unsafe function. Update CreateWrapperForExternal() to return a result struct with extra details. --- src/coreclr/src/interop/comwrappers.cpp | 38 +++++++--- src/coreclr/src/interop/comwrappers.h | 16 ++-- src/coreclr/src/interop/inc/interoplib.h | 21 ++++-- src/coreclr/src/interop/interoplib.cpp | 35 +++++---- .../src/interop/trackerobjectmanager.cpp | 33 +++------ src/coreclr/src/vm/interoplibinterface.cpp | 73 ++++++++++--------- 6 files changed, 117 insertions(+), 99 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 4475b4e46cf9c3..807c8f16106faf 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -539,7 +539,8 @@ ULONG ManagedObjectWrapper::Release(void) namespace { - const size_t ContextSentinal = 0x0a110ced; + const size_t LiveContextSentinal = 0x0a110ced; + const size_t DeadContextSentinal = 0xdeaddead; } NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_In_ void* cxtMaybe) @@ -552,7 +553,7 @@ NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_I NativeObjectWrapperContext* cxt = reinterpret_cast(cxtRaw); #ifdef _DEBUG - _ASSERTE(cxt->_sentinal == ContextSentinal); + _ASSERTE(cxt->_sentinal == LiveContextSentinal); #endif return cxt; @@ -586,13 +587,13 @@ HRESULT NativeObjectWrapperContext::Create( // Contract specifically requires zeroing out runtime context. std::memset(runtimeContext, 0, runtimeContextSize); - NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ trackerObject, runtimeContext }; + NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ runtimeContext, trackerObject }; - if (contextLocal->GetReferenceTracker() != nullptr) + if (trackerObject != nullptr) { // Inform the tracker object manager _ASSERTE((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject); - hr = TrackerObjectManager::AfterWrapperCreated(contextLocal); + hr = TrackerObjectManager::AfterWrapperCreated(trackerObject); if (FAILED(hr)) { Destroy(contextLocal); @@ -614,27 +615,40 @@ void NativeObjectWrapperContext::Destroy(_In_ NativeObjectWrapperContext* wrappe InteropLibImports::MemFree(wrapper, AllocScenario::NativeObjectWrapper); } -NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ void* runtimeContext) +NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ void* runtimeContext, _In_opt_ IReferenceTracker* trackerObject) : _trackerObject{ trackerObject } , _runtimeContext{ runtimeContext } + , _isValidTracker{ (trackerObject != nullptr ? TRUE : FALSE) } #ifdef _DEBUG - , _sentinal{ ContextSentinal } + , _sentinal{ LiveContextSentinal } #endif { - (void)_trackerObject->AddRef(); + if (_isValidTracker == TRUE) + (void)_trackerObject->AddRef(); } NativeObjectWrapperContext::~NativeObjectWrapperContext() { - (void)_trackerObject->Release(); + DisconnectTracker(); + +#ifdef _DEBUG + _sentinal = DeadContextSentinal; +#endif } -void* NativeObjectWrapperContext::GetRuntimeContext() const +void* NativeObjectWrapperContext::GetRuntimeContext() const noexcept { return _runtimeContext; } -IReferenceTracker* NativeObjectWrapperContext::GetReferenceTracker() const +IReferenceTracker* NativeObjectWrapperContext::GetReferenceTracker() const noexcept +{ + return ((_isValidTracker == TRUE) ? _trackerObject : nullptr); +} + +void NativeObjectWrapperContext::DisconnectTracker() noexcept { - return _trackerObject; + // Attempt to disconnect from the tracker. + if (TRUE == ::InterlockedCompareExchange(&_isValidTracker, FALSE, TRUE)) + (void)_trackerObject->Release(); } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 0f52bdffac9ed4..1c4fe463295f2d 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -117,11 +117,12 @@ ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); class NativeObjectWrapperContext { #ifdef _DEBUG - const size_t _sentinal; + size_t _sentinal; #endif IReferenceTracker* _trackerObject; void* _runtimeContext; + int32_t _isValidTracker; public: // static // Convert a context pointer into a NativeObjectWrapperContext. @@ -138,15 +139,18 @@ class NativeObjectWrapperContext static void Destroy(_In_ NativeObjectWrapperContext* wrapper); private: - NativeObjectWrapperContext(_In_ IReferenceTracker* trackerObject, _In_ void* runtimeContext); + NativeObjectWrapperContext(_In_ void* runtimeContext, _In_opt_ IReferenceTracker* trackerObject); ~NativeObjectWrapperContext(); public: // Get the associated runtime context for this context. - void* GetRuntimeContext() const; + void* GetRuntimeContext() const noexcept; // Get the IReferenceTracker instance. - IReferenceTracker* GetReferenceTracker() const; + IReferenceTracker* GetReferenceTracker() const noexcept; + + // Disconnect reference tracker instance. + void DisconnectTracker() noexcept; }; // API for creating an IAgileReference instance to a supplied IUnknown instance. @@ -171,10 +175,10 @@ class TrackerObjectManager static HRESULT OnIReferenceTrackerFound(_In_ IReferenceTracker* obj); // Called after wrapper has been created. - static HRESULT AfterWrapperCreated(_In_ NativeObjectWrapperContext* cxt); + static HRESULT AfterWrapperCreated(_In_ IReferenceTracker* obj); // Called before wrapper is about to be destroyed (the same lifetime as short weak handle). - static HRESULT BeforeWrapperDestroyed(_In_ NativeObjectWrapperContext* cxt); + static HRESULT BeforeWrapperDestroyed(_In_ IReferenceTracker* obj); public: // Begin the reference tracking process for external objects. diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index e3ab21d583e553..52870308bae879 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -36,22 +36,33 @@ namespace InteropLib // Destroy the supplied wrapper void DestroyWrapperForObject(_In_ void* wrapper) noexcept; + struct ExternalWrapperResult + { + // The returned context memory is guaranteed to be initialized to zero. + void* Context; + + // IAgileReference instance. + IUnknown* AgileRef; + + // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ for + // details. + bool FromTrackerRuntime; + }; + // Allocate a wrapper context for an external object. // The runtime supplies the external object, flags, and a memory // request in order to bring the object into the runtime. - // The returned context memory is guaranteed to be initialized to zero. HRESULT CreateWrapperForExternal( _In_ IUnknown* external, _In_ INT32 flags, _In_ size_t contextSize, - _Outptr_ void** context, - _Outptr_ IUnknown** agileReference) noexcept; + _Out_ ExternalWrapperResult* result) noexcept; // Destroy the supplied wrapper. void DestroyWrapperForExternal(_In_ void* context) noexcept; - // Check if the context is associated with a tracker runtime. - bool IsReferenceTrackerInstance(_In_ void* context) noexcept; + // Separate the supplied wrapper from the tracker runtime. + void SeparateWrapperFromTrackerRuntime(_In_ void* context) noexcept; // Register a runtime implementation callback in the Reference Tracker Host scenario. // Returns true if registration succeeded, otherwise false. diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index a68dca1b6c4376..486ce3f6cc5f19 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -61,24 +61,25 @@ namespace InteropLib _In_ IUnknown* external, _In_ INT32 flagsRaw, _In_ size_t contextSize, - _Outptr_ void** context, - _Outptr_ IUnknown** agileReference) noexcept + _Out_ ExternalWrapperResult* result) noexcept { - _ASSERTE(external != nullptr && context != nullptr && agileReference != nullptr); + _ASSERTE(external != nullptr && result != nullptr); HRESULT hr; + // Attempt to create an agile reference first. + ComHolder reference; + RETURN_IF_FAILED(CreateAgileReference(external, &reference)); + // Convert input to appropriate type. auto flags = static_cast(flagsRaw); NativeObjectWrapperContext* wrapperContext; RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); - ComHolder reference; - RETURN_IF_FAILED(CreateAgileReference(external, &reference)); - *agileReference = reference.Detach(); - - *context = wrapperContext->GetRuntimeContext(); + result->Context = wrapperContext->GetRuntimeContext(); + result->AgileReference = reference.Detach(); + result->FromTrackerRuntime = (wrapperContext->GetReferenceTracker() != nullptr); return S_OK; } @@ -90,11 +91,12 @@ namespace InteropLib _ASSERTE(context != nullptr); // Check if the tracker object manager should be informed prior to being destroyed. - if (context->GetReferenceTracker() != nullptr) + IReferenceTracker* trackerMaybe = context->GetReferenceTracker(); + if (trackerMaybe != nullptr) { // We only call this during a GC so ignore the failure as - // there is no way we can handle that failure at this point. - HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(context); + // there is no way we can handle it at this point. + HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(trackerMaybe); _ASSERTE(SUCCEEDED(hr)); (void)hr; } @@ -102,13 +104,14 @@ namespace InteropLib NativeObjectWrapperContext::Destroy(context); } - bool IsReferenceTrackerInstance(_In_ void* contextMaybe) noexcept + void SeparateWrapperFromTrackerRuntime(_In_ void* contextMaybe) noexcept { NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe); - if (context == nullptr) - return false; - return (context->GetReferenceTracker() != nullptr); + // A caller should not be separating a context without knowing if the context is valid. + _ASSERTE(context != nullptr); + + context->DisconnectTracker(); } bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept @@ -133,7 +136,7 @@ namespace InteropLib ULONG count = wrapper->AddRef(); if (count == 1) { - // No object was supplied so reactivation isn't possible. + // No object handle was supplied so reactivation isn't possible. if (handle == nullptr) { wrapper->Release(); diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 2c0c314b2a76d6..fea4c9d4a67dda 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -237,20 +237,6 @@ namespace } }; - HRESULT OnExternalTrackerObject(_In_ NativeObjectWrapperContext* nowc, _In_ RuntimeCallContext* cxt) - { - _ASSERTE(nowc != nullptr && cxt != nullptr); - - HRESULT hr; - IReferenceTracker* obj = nowc->GetReferenceTracker(); - - // Ask the tracker instance to find all reference targets. - FindDependentWrappersCallback cb{ nowc, cxt }; - RETURN_IF_FAILED(obj->FindTrackerTargets(&cb)); - - return S_OK; - } - HRESULT WalkExternalTrackerObjects(_In_ RuntimeCallContext* cxt) { _ASSERTE(cxt != nullptr); @@ -266,10 +252,13 @@ namespace NativeObjectWrapperContext* nowc = NativeObjectWrapperContext::MapFromRuntimeContext(extObjContext); // Check if the object is a tracker object. - if (nowc->GetReferenceTracker() == nullptr) + IReferenceTracker* trackerMaybe = nowc->GetReferenceTracker(); + if (trackerMaybe == nullptr) continue; - hr = OnExternalTrackerObject(nowc, cxt); + // Ask the tracker instance to find all reference targets. + FindDependentWrappersCallback cb{ nowc, cxt }; + hr = trackerMaybe->FindTrackerTargets(&cb); if (FAILED(hr)) break; } @@ -320,13 +309,11 @@ HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* o return S_OK; } -HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ NativeObjectWrapperContext* cxt) +HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ IReferenceTracker* obj) { - _ASSERTE(cxt != nullptr); + _ASSERTE(obj != nullptr); HRESULT hr; - IReferenceTracker* obj = cxt->GetReferenceTracker(); - _ASSERTE(obj != nullptr); // Notify tracker runtime that we've created a new wrapper for this object. // To avoid surprises, we should notify them before we fire the first AddRefFromTrackerSource. @@ -340,13 +327,11 @@ HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ NativeObjectWrapperContex return S_OK; } -HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ NativeObjectWrapperContext* cxt) +HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ IReferenceTracker* obj) { - _ASSERTE(cxt != nullptr); + _ASSERTE(obj != nullptr); HRESULT hr; - IReferenceTracker* obj = cxt->GetReferenceTracker(); - _ASSERTE(obj != nullptr); // Notify tracker runtime that we are about to destroy a wrapper // (same timing as short weak handle) for this object. diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 0d9010af410efa..93a1c2488544f8 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -34,7 +34,7 @@ namespace DWORD Flags; static void Construct( - _In_ ExternalObjectContext* cxt, + _Out_ ExternalObjectContext* cxt, _In_ IUnknown* identity, _In_opt_ void* threadContext, _In_ DWORD syncBlockIndex, @@ -94,34 +94,32 @@ namespace static_assert((sizeof(ExternalObjectContext) % sizeof(void*)) == 0, "Keep context pointer size aligned"); - // Holder for a External Object Context - struct ExtObjCxtHolder + // Holder for a External Wrapper Result + struct ExternalWrapperResultHolder { - ExternalObjectContext* _cxt; - ExtObjCxtHolder() - : _cxt(nullptr) + ExternalWrapperResult Result; + ExternalWrapperResultHolder() + : Result{} { } - ~ExtObjCxtHolder() + ~ExternalWrapperResultHolder() { - if (_cxt != nullptr) - InteropLib::Com::DestroyWrapperForExternal(_cxt); + if (Result.Context != nullptr) + InteropLib::Com::DestroyWrapperForExternal(Result.Context); + if (Result.AgileRef != nullptr) + (void)Result.AgileRef->Release(); } - ExternalObjectContext* operator->() + ExternalWrapperResult* operator&() { - return _cxt; + return &Result; } - ExternalObjectContext** operator&() + ExternalObjectContext* GetContext() { - return &_cxt; + return static_cast(Result.Context); } - operator ExternalObjectContext*() + ExternalObjectContext* DetachContext() { - return _cxt; - } - ExternalObjectContext* Detach() - { - ExternalObjectContext* t = _cxt; - _cxt = nullptr; + ExternalObjectContext* t = GetContext(); + Result.Context = nullptr; return t; } }; @@ -200,9 +198,7 @@ namespace // threadContext - The object must be associated with the supplied thread context. // // [TODO] Performance improvement should be made here to provide a custom IEnumerable - // instead of a managed array. When the custom solution is done, the objects' syncblocks - // should be updated by removing the IReferenceTracker instance stored within the - // context block held on the InteropLib side. + // instead of a managed array. OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext) { CONTRACT(OBJECTREF) @@ -250,6 +246,9 @@ namespace if (inst->ThreadContext == threadContext && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet(withFlags))) { + // Separate the wrapper from the tracker runtime + // prior to passing this onto the caller. + InteropLib::Com::SeparateWrapperFromTrackerRuntime(inst); gc.arrRef->SetAt(objCount, inst->GetObjectRef()); } } @@ -264,7 +263,9 @@ namespace void *src = gc.arrRef->GetDataPtr(); void *dest = gc.arrRefTmp->GetDataPtr(); - memcpyNoGCRefs(dest, src, objCount * elementSize); + + _ASSERTE(sizeof(Object*) == elementSize && "Assumption invalidated in memmoveGCRefs() usage"); + memmoveGCRefs(dest, src, objCount * elementSize); gc.arrRef = gc.arrRefTmp; } @@ -596,23 +597,26 @@ namespace else { // Create context and IAgileReference instance for the possibly new external COM object. - ExtObjCxtHolder newContext; - SafeComHolder agileRef; - hr = InteropLib::Com::CreateWrapperForExternal(identity, flags, sizeof(ExternalObjectContext), (void**)&newContext, &agileRef); + ExternalWrapperResultHolder resultHolder; + hr = InteropLib::Com::CreateWrapperForExternal( + identity, + flags, + sizeof(ExternalObjectContext), + &resultHolder); if (FAILED(hr)) COMPlusThrow(hr); // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, identity, agileRef, flags); + gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); // Construct the new context with the object details. - DWORD flags = InteropLib::Com::IsReferenceTrackerInstance((void*)newContext) + DWORD flags = resultHolder.Result.FromTrackerRuntime ? ExternalObjectContext::Flags_ReferenceTracker : ExternalObjectContext::Flags_None; ExternalObjectContext::Construct( - newContext, + resultHolder.GetContext(), identity, GetCurrentCtxCookie(), gc.objRef->GetSyncBlockIndex(), @@ -621,12 +625,12 @@ namespace // Attempt to insert the new context into the cache. { ExtObjCxtCache::LockHolder lock(cache); - extObjCxt = cache->FindOrAdd(identity, newContext); + extObjCxt = cache->FindOrAdd(identity, resultHolder.GetContext()); } // If the returned context matches the new context it means the // new context was inserted. - if (extObjCxt == newContext) + if (extObjCxt == resultHolder.GetContext()) { // Update the object's SyncBlock with a handle to the context for runtime cleanup. SyncBlock* syncBlock = gc.objRef->GetSyncBlock(); @@ -635,7 +639,7 @@ namespace (void)interopInfo->TrySetExternalComObjectContext((void**)extObjCxt); // Detach from the holder to avoid cleanup. - (void)newContext.Detach(); + (void)resultHolder.DetachContext(); } _ASSERTE(extObjCxt->IsActive()); @@ -1141,7 +1145,6 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context context->MarkCollected(); ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - _ASSERTE(cache != NULL); cache->Remove(context); } @@ -1183,8 +1186,6 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) if (nCondemnedGeneration >= 2) { ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - _ASSERTE(cache != NULL); - RCWRefCache *refCache = GetAppDomain()->GetRCWRefCache(); // Reset the ref cache From 4a583269baf9fab8e2a829abc303a612419faf7f Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 8 Feb 2020 21:03:41 -0800 Subject: [PATCH 31/83] Validation bug for converting to MOW. --- src/coreclr/src/interop/comwrappers.cpp | 4 +-- .../src/interop/trackerobjectmanager.cpp | 21 +++++++------- src/coreclr/src/vm/interoplibinterface.cpp | 29 +++++++------------ 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 807c8f16106faf..2d0dcd2d1164e4 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -297,8 +297,8 @@ ManagedObjectWrapper* ManagedObjectWrapper::MapFromIUnknown(_In_ IUnknown* pUnk) // If the first Vtable entry is part of the ManagedObjectWrapper IUnknown impl, // we know how to interpret the IUnknown. - void* firstEntryInVtable = *reinterpret_cast(pUnk); - if (firstEntryInVtable == ManagedObjectWrapper_IUnknownImpl.QueryInterface) + void** vtable = *reinterpret_cast(pUnk); + if (*vtable != ManagedObjectWrapper_IUnknownImpl.QueryInterface) return nullptr; ABI::ComInterfaceDispatch* disp = reinterpret_cast(pUnk); diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index fea4c9d4a67dda..2857662ee8a5e7 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -103,26 +103,27 @@ namespace return InteropLibImports::ReleaseExternalObjectsFromCurrentThread(impl); } + // Creates a proxy object (managed object wrapper) that points to the given IUnknown. + // The proxy represents the following: + // 1. Has a managed reference pointing to the external object + // and therefore forms a cycle that can be resolved by GC. + // 2. Forwards data binding requests. // - // Creates a proxy object that points to the given RCW - // The proxy - // 1. Has a managed reference pointing to the RCW, and therefore forms a cycle that can be resolved by GC - // 2. Forwards data binding requests // For example: // - // Grid <---- RCW Grid <-------- RCW + // Grid <---- NoCW Grid <-------- NoCW // | ^ | ^ // | | Becomes | | // v | v | // Rectangle Rectangle ----->Proxy // // Arguments - // obj - The identity IUnknown* where a RCW points to (Grid, in this case) - // Note that - // 1) we can either create a new RCW or get back an old one from cache - // 2) This obj could be a regular WinRT object (such as WinRT collection) for data binding + // obj - An IUnknown* where a NoCW points to (Grid, in this case) + // Notes: + // 1. We can either create a new NoCW or get back an old one from the cache. + // 2. This obj could be a regular tracker runtime object for data binding. // ppNewReference - The IReferenceTrackerTarget* for the proxy created - // Jupiter will call IReferenceTrackerTarget to establish a jupiter reference + // The tracker runtime will call IReferenceTrackerTarget to establish a reference. // STDMETHODIMP HostServices::GetTrackerTarget(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) { diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 93a1c2488544f8..274501daf4b594 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -772,8 +772,6 @@ namespace InteropLibImports } CONTRACTL_END; - ::OBJECTHANDLE implHandle = static_cast<::OBJECTHANDLE>(handle); - HRESULT hr = S_OK; BEGIN_EXTERNAL_ENTRYPOINT(&hr) { @@ -788,7 +786,7 @@ namespace InteropLibImports ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectFromHandle(implHandle); + gc.implRef = ObjectFromHandle(static_cast<::OBJECTHANDLE>(handle)); // Pass the objects along to get released. ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); @@ -815,8 +813,7 @@ namespace InteropLibImports } CONTRACTL_END; - ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle); - DestroyHandleCommon(objectHandle, InstanceHandleType); + DestroyHandleCommon(static_cast<::OBJECTHANDLE>(handle), InstanceHandleType); } bool GetGlobalPeggingState() noexcept @@ -862,14 +859,10 @@ namespace InteropLibImports CONTRACTL_END; HRESULT hr = S_OK; - ::OBJECTHANDLE implHandle = static_cast<::OBJECTHANDLE>(impl); - - *trackerTarget = NULL; - - // Switch to Cooperative mode since object references - // are being manipulated. BEGIN_EXTERNAL_ENTRYPOINT(&hr) { + // Switch to Cooperative mode since object references + // are being manipulated. GCX_COOP(); struct @@ -880,7 +873,7 @@ namespace InteropLibImports ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectFromHandle(implHandle); + gc.implRef = ObjectFromHandle(static_cast<::OBJECTHANDLE>(impl)); // Get wrapper for external object gc.objRef = GetOrCreateObjectForComInstanceInternal( @@ -903,18 +896,18 @@ namespace InteropLibImports struct RuntimeCallContext { - RuntimeCallContext(_In_ ExtObjCxtCache* cache, _In_ ExtObjCxtRefCache* refCache) - : Curr{ cache->_hashMap.Begin() } - , End{ cache->_hashMap.End() } - , RefCache{ refCache } - { } - // Iterators for all known external objects. ExtObjCxtCache::Iterator Curr; ExtObjCxtCache::Iterator End; // Pointer to cache used to create object references. ExtObjCxtRefCache* RefCache; + + RuntimeCallContext(_In_ ExtObjCxtCache* cache, _In_ ExtObjCxtRefCache* refCache) + : Curr{ cache->_hashMap.Begin() } + , End{ cache->_hashMap.End() } + , RefCache{ refCache } + { } }; HRESULT IteratorNext( From f40127f0db3c9f1c9847cd6ddb62d88dd9c62cd3 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sun, 9 Feb 2020 19:01:00 -0800 Subject: [PATCH 32/83] Move the ComInterfaceEntry to the ABI namespace. --- src/coreclr/src/interop/comwrappers.cpp | 32 +++++---- src/coreclr/src/interop/comwrappers.h | 22 +++--- src/coreclr/src/interop/interoplib.cpp | 4 +- src/coreclr/src/interop/platform.h | 3 + .../src/interop/trackerobjectmanager.cpp | 5 +- src/coreclr/src/vm/interoplibinterface.cpp | 71 +++++++++++++------ 6 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 2d0dcd2d1164e4..b926aa2d54c5e3 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -90,6 +90,12 @@ namespace ABI return section; } + struct ComInterfaceEntry + { + GUID IID; + const void* Vtable; + }; + struct EntrySet { const ComInterfaceEntry* start; @@ -309,19 +315,19 @@ HRESULT ManagedObjectWrapper::Create( _In_ CreateComInterfaceFlags flags, _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, - _In_ ComInterfaceEntry* userDefined, + _In_ ABI::ComInterfaceEntry* userDefined, _Outptr_ ManagedObjectWrapper** mow) { _ASSERTE(objectHandle != nullptr && mow != nullptr); // Maximum number of runtime supplied vtables. - ComInterfaceEntry runtimeDefinedLocal[4]; + ABI::ComInterfaceEntry runtimeDefinedLocal[4]; int32_t runtimeDefinedCount = 0; // Check if the caller will provide the IUnknown table. if ((flags & CreateComInterfaceFlags::CallerDefinedIUnknown) == CreateComInterfaceFlags::None) { - ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; + ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IUnknown); curr.Vtable = &ManagedObjectWrapper_IUnknownImpl; } @@ -329,7 +335,7 @@ HRESULT ManagedObjectWrapper::Create( // Check if the caller wants tracker support. if ((flags & CreateComInterfaceFlags::TrackerSupport) == CreateComInterfaceFlags::TrackerSupport) { - ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; + ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IReferenceTrackerTarget); curr.Vtable = &ManagedObjectWrapper_IReferenceTrackerTargetImpl; } @@ -337,7 +343,7 @@ HRESULT ManagedObjectWrapper::Create( _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefinedLocal)); // Compute size for ManagedObjectWrapper instance. - const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ComInterfaceEntry); + const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ABI::ComInterfaceEntry); const size_t totalDefinedCount = static_cast(runtimeDefinedCount) + userDefinedCount; // Compute the total entry size of dispatch section. @@ -353,11 +359,11 @@ HRESULT ManagedObjectWrapper::Create( char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper); // Copy in runtime supplied COM interface entries. - ComInterfaceEntry* runtimeDefined = nullptr; + ABI::ComInterfaceEntry* runtimeDefined = nullptr; if (0 < runtimeDefinedCount) { std::memcpy(runtimeDefinedOffset, runtimeDefinedLocal, totalRuntimeDefinedSize); - runtimeDefined = reinterpret_cast(runtimeDefinedOffset); + runtimeDefined = reinterpret_cast(runtimeDefinedOffset); } // Compute the dispatch section offset and ensure it is aligned. @@ -404,23 +410,23 @@ ManagedObjectWrapper::ManagedObjectWrapper( _In_ CreateComInterfaceFlags flags, _In_ OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, - _In_ const ComInterfaceEntry* runtimeDefined, + _In_ const ABI::ComInterfaceEntry* runtimeDefined, _In_ int32_t userDefinedCount, - _In_ const ComInterfaceEntry* userDefined, + _In_ const ABI::ComInterfaceEntry* userDefined, _In_ ABI::ComInterfaceDispatch* dispatches) : Target{ nullptr } , _runtimeDefinedCount{ runtimeDefinedCount } , _userDefinedCount{ userDefinedCount } , _runtimeDefined{ runtimeDefined } , _userDefined{ userDefined } - , _flags{ flags } , _dispatches{ dispatches } + , _refCount{ 1 } + , _flags{ flags } { bool wasSet = TrySetObjectHandle(objectHandle); _ASSERTE(wasSet); } - ManagedObjectWrapper::~ManagedObjectWrapper() { // If the target isn't null, then a managed object @@ -488,11 +494,13 @@ ULONG ManagedObjectWrapper::ReleaseFromReferenceTracker() HRESULT ManagedObjectWrapper::Peg() { + // [TODO] return E_NOTIMPL; } HRESULT ManagedObjectWrapper::Unpeg() { + // [TODO] return E_NOTIMPL; } @@ -649,6 +657,6 @@ IReferenceTracker* NativeObjectWrapperContext::GetReferenceTracker() const noexc void NativeObjectWrapperContext::DisconnectTracker() noexcept { // Attempt to disconnect from the tracker. - if (TRUE == ::InterlockedCompareExchange(&_isValidTracker, FALSE, TRUE)) + if (TRUE == ::InterlockedCompareExchange((LONG*)&_isValidTracker, FALSE, TRUE)) (void)_trackerObject->Release(); } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 1c4fe463295f2d..5abdd9ab84f980 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -27,16 +27,12 @@ enum class CreateObjectFlags DEFINE_ENUM_FLAG_OPERATORS(CreateObjectFlags); -struct ComInterfaceEntry -{ - GUID IID; - const void* Vtable; -}; -// Forward declaration +// Forward declarations namespace ABI { struct ComInterfaceDispatch; + struct ComInterfaceEntry; } // Class for wrapping a managed object and projecting it in a non-managed environment @@ -48,11 +44,11 @@ class ManagedObjectWrapper private: const int32_t _runtimeDefinedCount; const int32_t _userDefinedCount; - const ComInterfaceEntry* _runtimeDefined; - const ComInterfaceEntry* _userDefined; + const ABI::ComInterfaceEntry* _runtimeDefined; + const ABI::ComInterfaceEntry* _userDefined; ABI::ComInterfaceDispatch* _dispatches; - LONGLONG _refCount = 1; + LONGLONG _refCount; const CreateComInterfaceFlags _flags; public: // static @@ -71,7 +67,7 @@ class ManagedObjectWrapper _In_ CreateComInterfaceFlags flags, _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, - _In_ ComInterfaceEntry* userDefined, + _In_ ABI::ComInterfaceEntry* userDefined, _Outptr_ ManagedObjectWrapper** mow); // Destroy the instance @@ -82,9 +78,9 @@ class ManagedObjectWrapper _In_ CreateComInterfaceFlags flags, _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, - _In_ const ComInterfaceEntry* runtimeDefined, + _In_ const ABI::ComInterfaceEntry* runtimeDefined, _In_ int32_t userDefinedCount, - _In_ const ComInterfaceEntry* userDefined, + _In_ const ABI::ComInterfaceEntry* userDefined, _In_ ABI::ComInterfaceDispatch* dispatches); ~ManagedObjectWrapper(); @@ -122,7 +118,7 @@ class NativeObjectWrapperContext IReferenceTracker* _trackerObject; void* _runtimeContext; - int32_t _isValidTracker; + Volatile _isValidTracker; public: // static // Convert a context pointer into a NativeObjectWrapperContext. diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index 486ce3f6cc5f19..9157626b1b00d2 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -38,7 +38,7 @@ namespace InteropLib // Convert inputs to appropriate types. auto flags = static_cast(flagsRaw); - auto vtables = static_cast(vtablesRaw); + auto vtables = static_cast(vtablesRaw); ManagedObjectWrapper* mow; RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow)); @@ -78,7 +78,7 @@ namespace InteropLib RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); result->Context = wrapperContext->GetRuntimeContext(); - result->AgileReference = reference.Detach(); + result->AgileRef = reference.Detach(); result->FromTrackerRuntime = (wrapperContext->GetReferenceTracker() != nullptr); return S_OK; } diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index f3e08ba6f7c791..3cc4f70e2d130b 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -25,4 +25,7 @@ #define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") +// Runtime headers +#include + #endif // _INTEROP_PLATFORM_H_ \ No newline at end of file diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 2857662ee8a5e7..9896f0a3ff835e 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// Runtime headers -#include - #include "comwrappers.h" #include @@ -190,7 +187,7 @@ namespace { HRESULT hr; - if (target != nullptr) + if (target == nullptr) return E_POINTER; ManagedObjectWrapper* mow = ManagedObjectWrapper::MapFromIUnknown(target); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 274501daf4b594..05c2e2273135ee 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -64,7 +64,7 @@ namespace bool IsActive() const { - return IsSet(Flags_Collected) + return !IsSet(Flags_Collected) && (SyncBlockIndex != InvalidSyncBlockIndex); } @@ -97,7 +97,7 @@ namespace // Holder for a External Wrapper Result struct ExternalWrapperResultHolder { - ExternalWrapperResult Result; + InteropLib::Com::ExternalWrapperResult Result; ExternalWrapperResultHolder() : Result{} { } @@ -108,7 +108,7 @@ namespace if (Result.AgileRef != nullptr) (void)Result.AgileRef->Release(); } - ExternalWrapperResult* operator&() + InteropLib::Com::ExternalWrapperResult* operator&() { return &Result; } @@ -129,8 +129,29 @@ namespace static Volatile g_Instance; public: // static + static ExtObjCxtCache* GetInstanceNoThrow() noexcept + { + CONTRACT(ExtObjCxtCache*) + { + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + + RETURN g_Instance; + } + static ExtObjCxtCache* GetInstance() { + CONTRACT(ExtObjCxtCache*) + { + THROWS; + GC_NOTRIGGER; + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + if (g_Instance.Load() == NULL) { ExtObjCxtCache* instMaybe = new ExtObjCxtCache(); @@ -140,7 +161,7 @@ namespace delete instMaybe; } - return g_Instance; + RETURN g_Instance; } public: // Inner class definitions @@ -377,7 +398,6 @@ namespace CONTRACTL { THROWS; - GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(implPROTECTED != NULL); PRECONDITION(instancePROTECTED != NULL); @@ -407,7 +427,6 @@ namespace CONTRACTL { THROWS; - GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(implPROTECTED != NULL); PRECONDITION(externalComObject != NULL); @@ -434,7 +453,6 @@ namespace CONTRACTL { THROWS; - GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(implPROTECTED != NULL); PRECONDITION(objsEnumPROTECTED != NULL); @@ -456,7 +474,6 @@ namespace CONTRACT(void*) { THROWS; - GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(impl != NULL); PRECONDITION(instance != NULL); @@ -561,7 +578,6 @@ namespace CONTRACT(OBJECTREF) { THROWS; - GC_TRIGGERS; MODE_COOPERATIVE; PRECONDITION(impl != NULL); PRECONDITION(identity != NULL); @@ -658,6 +674,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; + GC_NOTRIGGER; MODE_ANY; PRECONDITION(sizeInBytes != 0); } @@ -671,6 +688,8 @@ namespace InteropLibImports CONTRACTL { NOTHROW; + GC_NOTRIGGER; + MODE_ANY; PRECONDITION(mem != NULL); } CONTRACTL_END; @@ -789,7 +808,7 @@ namespace InteropLibImports gc.implRef = ObjectFromHandle(static_cast<::OBJECTHANDLE>(handle)); // Pass the objects along to get released. - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); gc.objsEnumRef = cache->CreateManagedEnumerable( ExternalObjectContext::Flags_ReferenceTracker, GetCurrentCtxCookie()); @@ -808,6 +827,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; + GC_NOTRIGGER; MODE_ANY; PRECONDITION(handle != NULL); } @@ -821,6 +841,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -833,6 +854,7 @@ namespace InteropLibImports CONTRACTL { NOTHROW; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -966,7 +988,7 @@ namespace InteropLibImports OBJECTREF target = ObjectFromHandle(objectHandle); // If these point at the same object don't create a reference. - if (source->GetSyncBlock() == target->GetSyncBlock()) + if (source->PassiveGetSyncBlock() == target->PassiveGetSyncBlock()) return S_FALSE; return runtimeContext->RefCache->AddReferenceFromObjectToObject(source, target); @@ -1137,7 +1159,7 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context _ASSERTE(context->IsActive()); context->MarkCollected(); - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); cache->Remove(context); } @@ -1178,18 +1200,25 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) // See Interop::OnGCFinished() if (nCondemnedGeneration >= 2) { - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - RCWRefCache *refCache = GetAppDomain()->GetRCWRefCache(); + EX_TRY + { + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + RCWRefCache *refCache = GetAppDomain()->GetRCWRefCache(); - // Reset the ref cache - refCache->ResetDependentHandles(); + // Reset the ref cache + refCache->ResetDependentHandles(); - // Create a call context for the InteropLib. - InteropLibImports::RuntimeCallContext cxt(cache, refCache); - (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt); + // Create a call context for the InteropLib. + InteropLibImports::RuntimeCallContext cxt(cache, refCache); + (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt); - // Shrink cache and clear unused handles. - refCache->ShrinkDependentHandles(); + // Shrink cache and clear unused handles. + refCache->ShrinkDependentHandles(); + } + EX_CATCH + { + } + EX_END_CATCH(RethrowCorruptingExceptions) } #endif // FEATURE_COMINTEROP From a023e8589db5a3f61a1be8c7890651df3930aa2b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 10 Feb 2020 15:56:10 -0800 Subject: [PATCH 33/83] Mock Tracker Runtime impl --- .../CMakeLists.txt | 2 +- .../MockReferenceTrackerRuntime/Exports.def | 2 - .../ReferenceTrackerRuntime.cpp | 161 ++++++++++++------ .../ReferenceTrackerRuntime.h | 21 --- .../src/Interop/COM/ComWrappers/Program.cs | 102 ++++++++++- 5 files changed, 211 insertions(+), 77 deletions(-) delete mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def delete mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt index 84222f3990b751..fb232b99da3e17 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt @@ -1,6 +1,6 @@ project (MockReferenceTrackerRuntime) include_directories( ${INC_PLATFORM_DIR} ) -set(SOURCES ReferenceTrackerRuntime.cpp Exports.def) +set(SOURCES ReferenceTrackerRuntime.cpp) # add the shared library add_library (MockReferenceTrackerRuntime SHARED ${SOURCES}) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def deleted file mode 100644 index 441edaa216ca54..00000000000000 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/Exports.def +++ /dev/null @@ -1,2 +0,0 @@ -EXPORTS - CreateExternalObject PRIVATE \ No newline at end of file diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp index 7109705801ce47..06557b2e7f9a43 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -2,31 +2,87 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include #include #include +#include + +namespace API +{ + // Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ + class DECLSPEC_UUID("64bd43f8-bfee-4ec4-b7eb-2935158dae21") IReferenceTrackerTarget : public IUnknown + { + public: + STDMETHOD_(ULONG, AddRefFromReferenceTracker)() = 0; + STDMETHOD_(ULONG, ReleaseFromReferenceTracker)() = 0; + STDMETHOD(Peg)() = 0; + STDMETHOD(Unpeg)() = 0; + }; + + class DECLSPEC_UUID("29a71c6a-3c42-4416-a39d-e2825a07a773") IReferenceTrackerHost : public IUnknown + { + public: + STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags) = 0; + STDMETHOD(ReleaseDisconnectedReferenceSources)() = 0; + STDMETHOD(NotifyEndOfReferenceTrackingOnThread)() = 0; + STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) = 0; + STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated) = 0; + STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated) = 0; + }; + + class DECLSPEC_UUID("3cf184b4-7ccb-4dda-8455-7e6ce99a3298") IReferenceTrackerManager : public IUnknown + { + public: + STDMETHOD(ReferenceTrackingStarted)() = 0; + STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) = 0; + STDMETHOD(ReferenceTrackingCompleted)() = 0; + STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost *pCLRServices) = 0; + }; + + class DECLSPEC_UUID("04b3486c-4687-4229-8d14-505ab584dd88") IFindReferenceTargetsCallback : public IUnknown + { + public: + STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) = 0; + }; + + class DECLSPEC_UUID("11d3b13a-180e-4789-a8be-7712882893e6") IReferenceTracker : public IUnknown + { + public: + STDMETHOD(ConnectFromTrackerSource)() = 0; + STDMETHOD(DisconnectFromTrackerSource)() = 0; + STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback *pCallback) = 0; + STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager **ppTrackerManager) = 0; + STDMETHOD(AddRefFromTrackerSource)() = 0; + STDMETHOD(ReleaseFromTrackerSource)() = 0; + STDMETHOD(PegFromTrackerSource)() = 0; + }; +} namespace { - class TrackerRuntimeManagerImpl : public IReferenceTrackerManager + class TrackerRuntimeManagerImpl : public API::IReferenceTrackerManager { - CComPtr runtimeServices; + CComPtr runtimeServices; public: STDMETHOD(ReferenceTrackingStarted)() { - return E_NOTIMPL; + // [TODO] + return S_OK; } STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) { - return E_NOTIMPL; + // [TODO] + return S_OK; } STDMETHOD(ReferenceTrackingCompleted)() { - return E_NOTIMPL; + // [TODO] + return S_OK; } - STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost* pHostServices) + STDMETHOD(SetReferenceTrackerHost)(_In_ API::IReferenceTrackerHost* pHostServices) { assert(pHostServices != nullptr); return pHostServices->QueryInterface(&runtimeServices); @@ -43,9 +99,9 @@ namespace if (ppvObject == nullptr) return E_POINTER; - if (IsEqualIID(riid, __uuidof(IReferenceTrackerManager))) + if (IsEqualIID(riid, __uuidof(API::IReferenceTrackerManager))) { - *ppvObject = static_cast(this); + *ppvObject = static_cast(this); } else if (IsEqualIID(riid, IID_IUnknown)) { @@ -64,68 +120,74 @@ namespace TrackerRuntimeManagerImpl TrackerRuntimeManager; + // Testing types struct DECLSPEC_UUID("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09") ITest : public IUnknown { STDMETHOD(SetValue)(int i) = 0; }; - struct ExternalObject : public IExternalObject, public IReferenceTracker, public UnknownImpl + struct DECLSPEC_UUID("42951130-245C-485E-B60B-4ED4254256F8") ITrackerObject : public IUnknown + { + STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id) = 0; + STDMETHOD(DropObjectRef)(_In_ int id) = 0; + }; + + struct TrackerObject : public ITrackerObject, public API::IReferenceTracker, public UnknownImpl { const size_t _id; - std::atomic _trackerCount; + std::atomic _trackerSourceCount; bool _connected; - std::unordered_map> _elements; + std::atomic _elementId; + std::unordered_map> _elements; - ExternalObject(size_t id) : _id{ id }, _trackerCount{0}, _connected{ false } + TrackerObject(size_t id) : _id{ id }, _trackerSourceCount{ 0 }, _connected{ false }, _elementId{ 1 } { } - STDMETHOD(AddObjectRef)(_In_ IUnknown* c) + STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id) { - assert(c != nullptr); + assert(c != nullptr && id != nullptr); try { - if (!_elements.insert(std::make_pair(size_t(c), CComPtr{ c })).second) + *id = _elementId; + if (!_elements.insert(std::make_pair(*id, CComPtr{ c })).second) return S_FALSE; + + _elementId++; } catch (const std::bad_alloc&) { return E_OUTOFMEMORY; } - CComPtr mowMaybe; + CComPtr mowMaybe; if (S_OK == c->QueryInterface(&mowMaybe)) (void)mowMaybe->AddRefFromReferenceTracker(); return S_OK; } - STDMETHOD(DropObjectRef)(_In_ IUnknown* c) + STDMETHOD(DropObjectRef)(_In_ int id) { - assert(c != nullptr); - auto erased_count = _elements.erase((size_t)c); - if (erased_count > 0) + auto iter = _elements.find(id); + if (iter == std::end(_elements)) + return S_FALSE; + + CComPtr mowMaybe; + if (S_OK == iter->second->QueryInterface(&mowMaybe)) { - CComPtr mowMaybe; - if (S_OK == c->QueryInterface(&mowMaybe)) - { - for (decltype(erased_count) i = 0; i < erased_count; ++i) - (void)mowMaybe->ReleaseFromReferenceTracker(); - } + (void)mowMaybe->ReleaseFromReferenceTracker(); } - return S_OK; - } + _elements.erase(iter); - STDMETHOD(UseObjectRefs)() - { - return E_NOTIMPL; + return S_OK; } STDMETHOD(ConnectFromTrackerSource)(); STDMETHOD(DisconnectFromTrackerSource)(); - STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback* pCallback); - STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager** ppTrackerManager); + STDMETHOD(FindTrackerTargets)(_In_ API::IFindReferenceTargetsCallback* pCallback); + STDMETHOD(GetReferenceTrackerManager)(_Outptr_ API::IReferenceTrackerManager** ppTrackerManager); STDMETHOD(AddRefFromTrackerSource)(); STDMETHOD(ReleaseFromTrackerSource)(); STDMETHOD(PegFromTrackerSource)(); @@ -134,29 +196,29 @@ namespace /* [in] */ REFIID riid, /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) { - return DoQueryInterface(riid, ppvObject, static_cast(this)); + return DoQueryInterface(riid, ppvObject, static_cast(this), static_cast(this)); } DEFINE_REF_COUNTING() }; - HRESULT STDMETHODCALLTYPE ExternalObject::ConnectFromTrackerSource() + HRESULT STDMETHODCALLTYPE TrackerObject::ConnectFromTrackerSource() { _connected = true; return S_OK; } - HRESULT STDMETHODCALLTYPE ExternalObject::DisconnectFromTrackerSource() + HRESULT STDMETHODCALLTYPE TrackerObject::DisconnectFromTrackerSource() { _connected = false; return S_OK; } - HRESULT STDMETHODCALLTYPE ExternalObject::FindTrackerTargets(_In_ IFindReferenceTargetsCallback* pCallback) + HRESULT STDMETHODCALLTYPE TrackerObject::FindTrackerTargets(_In_ API::IFindReferenceTargetsCallback* pCallback) { assert(pCallback != nullptr); - CComPtr mowMaybe; + CComPtr mowMaybe; for (auto &e : _elements) { if (S_OK == e.second->QueryInterface(&mowMaybe)) @@ -169,27 +231,27 @@ namespace return S_OK; } - HRESULT STDMETHODCALLTYPE ExternalObject::GetReferenceTrackerManager(_Outptr_ IReferenceTrackerManager** ppTrackerManager) + HRESULT STDMETHODCALLTYPE TrackerObject::GetReferenceTrackerManager(_Outptr_ API::IReferenceTrackerManager** ppTrackerManager) { assert(ppTrackerManager != nullptr); - return TrackerRuntimeManager.QueryInterface(__uuidof(IReferenceTrackerManager), (void**)ppTrackerManager); + return TrackerRuntimeManager.QueryInterface(__uuidof(API::IReferenceTrackerManager), (void**)ppTrackerManager); } - HRESULT STDMETHODCALLTYPE ExternalObject::AddRefFromTrackerSource() + HRESULT STDMETHODCALLTYPE TrackerObject::AddRefFromTrackerSource() { - assert(0 <= _trackerCount); - ++_trackerCount; + assert(0 <= _trackerSourceCount); + ++_trackerSourceCount; return S_OK; } - HRESULT STDMETHODCALLTYPE ExternalObject::ReleaseFromTrackerSource() + HRESULT STDMETHODCALLTYPE TrackerObject::ReleaseFromTrackerSource() { - assert(0 < _trackerCount); - --_trackerCount; + assert(0 < _trackerSourceCount); + --_trackerSourceCount; return S_OK; } - HRESULT STDMETHODCALLTYPE ExternalObject::PegFromTrackerSource() + HRESULT STDMETHODCALLTYPE TrackerObject::PegFromTrackerSource() { /* Not used by runtime */ return E_NOTIMPL; @@ -198,7 +260,8 @@ namespace std::atomic CurrentObjectId{}; } -IExternalObject* STDMETHODCALLTYPE CreateExternalObject() +// Create external object +extern "C" DLL_EXPORT ITrackerObject* STDMETHODCALLTYPE CreateTrackerObject() { - return new ExternalObject{ CurrentObjectId++ }; + return new TrackerObject{ CurrentObjectId++ }; } diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h deleted file mode 100644 index 830c0df4ea9093..00000000000000 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.h +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// - -#ifndef _EXTERNAL_H_ -#define _EXTERNAL_H_ - -#include - -struct DECLSPEC_UUID("42951130-245C-485E-B60B-4ED4254256F8") IExternalObject : public IUnknown -{ - STDMETHOD(AddObjectRef)(_In_ IUnknown* c) = 0; - STDMETHOD(DropObjectRef)(_In_ IUnknown * c) = 0; - STDMETHOD(UseObjectRefs)() = 0; -}; - -// Create external object -EXPORT IExternalObject* STDMETHODCALLTYPE CreateExternalObject(); - -#endif // _EXTERNAL_H_ diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index b8ea2d8f02cc17..5770ee63a39227 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -13,6 +13,9 @@ namespace ComWrappersTests class Program { + // + // Managed object with native wrapper definition. + // [Guid("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09")] interface ITest { @@ -58,6 +61,83 @@ public static int SetValueInternal(IntPtr dispatchPtr, int i) } } + // + // Native interface defintion with managed wrapper for tracker object + // + struct MockReferenceTrackerRuntime + { + [DllImport(nameof(MockReferenceTrackerRuntime))] + extern public static IntPtr CreateTrackerObject(); + } + + [Guid("42951130-245C-485E-B60B-4ED4254256F8")] + public interface ITrackerObject + { + int AddObjectRef(IntPtr obj); + void DropObjectRef(int id); + }; + + public struct VtblPtr + { + public IntPtr Vtbl; + } + + public class ITrackerObjectWrapper : ITrackerObject + { + private struct ITrackerObjectWrapperVtbl + { + public IntPtr QueryInterface; + public _AddRef AddRef; + public _Release Release; + public _AddObjectRef AddObjectRef; + public _DropObjectRef DropObjectRef; + } + + private delegate int _AddRef(IntPtr This); + private delegate int _Release(IntPtr This); + private delegate int _AddObjectRef(IntPtr This, IntPtr obj, out int id); + private delegate int _DropObjectRef(IntPtr This, int id); + + private readonly IntPtr instance; + private readonly ITrackerObjectWrapperVtbl vtable; + + public ITrackerObjectWrapper(IntPtr instance) + { + var inst = Marshal.PtrToStructure(instance); + this.vtable = Marshal.PtrToStructure(inst.Vtbl); + this.instance = instance; + } + + ~ITrackerObjectWrapper() + { + if (this.instance != IntPtr.Zero) + { + this.vtable.Release(this.instance); + } + } + + public int AddObjectRef(IntPtr obj) + { + int id; + int hr = this.vtable.AddObjectRef(this.instance, obj, out id); + if (hr != 0) + { + throw new COMException($"{nameof(AddObjectRef)}", hr); + } + + return id; + } + + public void DropObjectRef(int id) + { + int hr = this.vtable.DropObjectRef(this.instance, id); + if (hr != 0) + { + throw new COMException($"{nameof(DropObjectRef)}", hr); + } + } + } + class MyComWrappers : ComWrappers { protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) @@ -90,13 +170,22 @@ class MyComWrappers : ComWrappers return entryRaw; } - protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) { - throw new NotImplementedException(); + Assert.AreNotEqual(agileObj, IntPtr.Zero); + + var iid = typeof(ITrackerObject).GUID; + IntPtr iTestComObject; + int hr = Marshal.QueryInterface(externalComObject, ref iid, out iTestComObject); + Assert.AreEqual(hr, 0); + + return new ITrackerObjectWrapper(iTestComObject); } public static void ValidateIUnknownImpls() { + Console.WriteLine($"Running {nameof(ValidateIUnknownImpls)}..."); + ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); Assert.AreNotEqual(fpQueryInteface, IntPtr.Zero); @@ -107,6 +196,8 @@ public static void ValidateIUnknownImpls() static void ValidateComInterfaceCreation() { + Console.WriteLine($"Running {nameof(ValidateComInterfaceCreation)}..."); + var testObj = new Test(); var wrappers = new MyComWrappers(); @@ -127,8 +218,9 @@ static void ValidateComInterfaceCreation() // Create a new wrapper IntPtr comWrapperNew = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); - Assert.AreNotEqual(comWrapper, IntPtr.Zero); - Assert.AreNotEqual(comWrapperNew, comWrapper); + + // Once a wrapper is created for a managed object it is always present + Assert.AreEqual(comWrapperNew, comWrapper); // Release the new wrapper count = Marshal.Release(comWrapperNew); @@ -140,6 +232,8 @@ static void ValidateIUnknownImpls() static void ValidateRegisterForReferenceTrackerHost() { + Console.WriteLine($"Running {nameof(ValidateRegisterForReferenceTrackerHost)}..."); + var wrappers1 = new MyComWrappers(); wrappers1.RegisterForReferenceTrackerHost(); From 942b19e1135d3459a8603313878ff906b11b1fb0 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 11 Feb 2020 12:53:49 -0800 Subject: [PATCH 34/83] Forward declare Win32 API. --- src/coreclr/src/interop/agilereferences.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp index 4702e8ff7591eb..2971766b0a2a81 100644 --- a/src/coreclr/src/interop/agilereferences.cpp +++ b/src/coreclr/src/interop/agilereferences.cpp @@ -7,26 +7,20 @@ #include "comwrappers.h" -#if (NTDDI_VERSION >= NTDDI_WINBLUE) -#include - -#else -// Forward declare if OS verion is not set high enough. +// Forward declare the Win32 API. enum AgileReferenceOptions { AGILEREFERENCE_DEFAULT = 0, AGILEREFERENCE_DELAYEDMARSHAL = 1, }; -WINOLEAPI RoGetAgileReference( +EXTERN_C HRESULT STDAPICALLTYPE RoGetAgileReference( _In_ enum AgileReferenceOptions options, _In_ REFIID riid, _In_ IUnknown* pUnk, _COM_Outptr_ IAgileReference** ppAgileReference ); -#endif - namespace { // Global function pointer for RoGetAgileReference From 76eae00b904e4c28a21e2729d86dd23f7de50630 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 11 Feb 2020 12:55:56 -0800 Subject: [PATCH 35/83] Spelling mistake. --- src/coreclr/src/interop/comwrappers.cpp | 10 +++++----- src/coreclr/src/interop/comwrappers.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index b926aa2d54c5e3..f67c35a0a81d0a 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -547,8 +547,8 @@ ULONG ManagedObjectWrapper::Release(void) namespace { - const size_t LiveContextSentinal = 0x0a110ced; - const size_t DeadContextSentinal = 0xdeaddead; + const size_t LiveContextSentinel = 0x0a110ced; + const size_t DeadContextSentinel = 0xdeaddead; } NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_In_ void* cxtMaybe) @@ -561,7 +561,7 @@ NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_I NativeObjectWrapperContext* cxt = reinterpret_cast(cxtRaw); #ifdef _DEBUG - _ASSERTE(cxt->_sentinal == LiveContextSentinal); + _ASSERTE(cxt->_sentinel == LiveContextSentinel); #endif return cxt; @@ -628,7 +628,7 @@ NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ void* runtimeContext , _runtimeContext{ runtimeContext } , _isValidTracker{ (trackerObject != nullptr ? TRUE : FALSE) } #ifdef _DEBUG - , _sentinal{ LiveContextSentinal } + , _sentinel{ LiveContextSentinel } #endif { if (_isValidTracker == TRUE) @@ -640,7 +640,7 @@ NativeObjectWrapperContext::~NativeObjectWrapperContext() DisconnectTracker(); #ifdef _DEBUG - _sentinal = DeadContextSentinal; + _sentinel = DeadContextSentinel; #endif } diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 5abdd9ab84f980..38ffbf364756de 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -113,7 +113,7 @@ ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0); class NativeObjectWrapperContext { #ifdef _DEBUG - size_t _sentinal; + size_t _sentinel; #endif IReferenceTracker* _trackerObject; From fa9af01daf04162f8000528ec3691d4efa6ffafd Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 11 Feb 2020 15:20:16 -0800 Subject: [PATCH 36/83] Move registration of global ComWrappers to be entirely in managed code. --- .../Runtime/InteropServices/ComWrappers.cs | 28 ++++++----- src/coreclr/src/interop/comwrappers.h | 5 -- src/coreclr/src/interop/inc/interoplib.h | 4 -- .../src/interop/inc/interoplibimports.h | 6 +-- src/coreclr/src/interop/interoplib.cpp | 5 -- .../src/interop/trackerobjectmanager.cpp | 25 +--------- src/coreclr/src/vm/ecalllist.h | 1 - src/coreclr/src/vm/interoplibinterface.cpp | 49 ++----------------- src/coreclr/src/vm/interoplibinterface.h | 3 -- 9 files changed, 25 insertions(+), 101 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 148f0978e11750..2a21e1b918de67 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections; +using System.Threading; using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; @@ -99,6 +101,11 @@ private struct ComInterfaceInstance } } + /// + /// Globally registered instance of the ComWrappers class. + /// + private static ComWrappers? s_GlobalInstance; + /// /// Create an COM representation of the supplied object that can be passed to an non-managed environment. /// @@ -131,8 +138,8 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); // Call to execute the abstract instance function - internal static unsafe void* CallComputeVtables(ComWrappers comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) - => comWrappersImpl.ComputeVtables(obj, flags, out count); + internal static unsafe void* CallComputeVtables(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) + => (comWrappersImpl ?? s_GlobalInstance!).ComputeVtables(obj, flags, out count); /// /// Get the currently registered managed object or creates a new managed object and registers it. @@ -168,8 +175,8 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object CallCreateObject(ComWrappers comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) - => comWrappersImpl.CreateObject(externalComObject, agileObjectRef, flags); + internal static object CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) + => (comWrappersImpl ?? s_GlobalInstance!).CreateObject(externalComObject, agileObjectRef, flags); /// /// Called when a request is made for a collection of objects to be released. @@ -184,8 +191,8 @@ protected virtual void ReleaseObjects(IEnumerable objects) } // Call to execute the virtual instance function - internal static void CallReleaseObjects(ComWrappers comWrappersImpl, IEnumerable objects) - => comWrappersImpl.ReleaseObjects(objects); + internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects) + => (comWrappersImpl ?? s_GlobalInstance!).ReleaseObjects(objects); /// /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. @@ -196,13 +203,12 @@ internal static void CallReleaseObjects(ComWrappers comWrappersImpl, IEnumerable /// public void RegisterForReferenceTrackerHost() { - ComWrappers impl = this; - RegisterForReferenceTrackerHostInternal(ObjectHandleOnStack.Create(ref impl)); + if (null != Interlocked.CompareExchange(ref s_GlobalInstance, this, null)) + { + throw new InvalidOperationException(); + } } - [DllImport(RuntimeHelpers.QCall)] - private static extern void RegisterForReferenceTrackerHostInternal(ObjectHandleOnStack comWrappersImpl); - /// /// Get the runtime provided IUnknown implementation. /// diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 38ffbf364756de..8205558d10716c 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -162,11 +162,6 @@ HRESULT CreateAgileReference( class TrackerObjectManager { public: - // Attempt to set a runtime implementation for use by the IReferenceTrackerHost implementation. - static bool TrySetReferenceTrackerHostRuntimeImpl( - _In_ InteropLib::OBJECTHANDLE objectHandle, - _In_ InteropLib::OBJECTHANDLE current = nullptr); - // Called when an IReferenceTracker instance is found. static HRESULT OnIReferenceTrackerFound(_In_ IReferenceTracker* obj); diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 52870308bae879..c9526c4f463c8d 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -64,10 +64,6 @@ namespace InteropLib // Separate the supplied wrapper from the tracker runtime. void SeparateWrapperFromTrackerRuntime(_In_ void* context) noexcept; - // Register a runtime implementation callback in the Reference Tracker Host scenario. - // Returns true if registration succeeded, otherwise false. - bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept; - // Get internal interop IUnknown dispatch pointers. void GetIUnknownImpl( _Out_ void** fpQueryInterface, diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 216c98b782c5b8..3c9dfdf103bc41 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -40,7 +40,7 @@ namespace InteropLibImports HRESULT WaitForRuntimeFinalizerForExternal() noexcept; // Release objects associated with the current thread. - HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept; + HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept; // Delete Object instance handle void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; @@ -65,11 +65,9 @@ namespace InteropLibImports _In_ void* extObjContext, _In_ InteropLib::OBJECTHANDLE handle) noexcept; - // Given a runtime implementation, get or create - // an IReferenceTrackerTarget instance for the supplied + // Get or create an IReferenceTrackerTarget instance for the supplied // external object. HRESULT GetOrCreateTrackerTargetForExternal( - _In_ InteropLib::OBJECTHANDLE impl, _In_ IUnknown* externalComObject, _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index 9157626b1b00d2..923611e6456b94 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -114,11 +114,6 @@ namespace InteropLib context->DisconnectTracker(); } - bool RegisterReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle) noexcept - { - return TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(objectHandle); - } - void GetIUnknownImpl( _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 9896f0a3ff835e..df55b8df8abfb0 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -20,9 +20,6 @@ namespace // the HostServices class should have no instance fields. class HostServices : public IReferenceTrackerHost { - public: // static - static Volatile RuntimeImpl; - public: // IReferenceTrackerHost STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags); STDMETHOD(ReleaseDisconnectedReferenceSources)(); @@ -62,9 +59,6 @@ namespace } }; - // Runtime implementation for some host services. - Volatile HostServices::RuntimeImpl; - // Global instance of host services. HostServices g_HostServicesInstance; @@ -93,11 +87,7 @@ namespace STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread() { - OBJECTHANDLE impl = HostServices::RuntimeImpl; - if (impl == nullptr) - return E_NOT_SET; - - return InteropLibImports::ReleaseExternalObjectsFromCurrentThread(impl); + return InteropLibImports::ReleaseExternalObjectsFromCurrentThread(); } // Creates a proxy object (managed object wrapper) that points to the given IUnknown. @@ -129,10 +119,6 @@ namespace HRESULT hr; - OBJECTHANDLE impl = HostServices::RuntimeImpl; - if (impl == nullptr) - return E_NOT_SET; - // QI for IUnknown to get the identity unknown ComHolder identity; RETURN_IF_FAILED(obj->QueryInterface(&identity)); @@ -140,7 +126,6 @@ namespace // Get or create an existing implementation for this external. ComHolder target; RETURN_IF_FAILED(InteropLibImports::GetOrCreateTrackerTargetForExternal( - impl, identity, (INT32)CreateObjectFlags::TrackerObject, (INT32)CreateComInterfaceFlags::TrackerSupport, @@ -275,14 +260,6 @@ namespace } } -bool TrackerObjectManager::TrySetReferenceTrackerHostRuntimeImpl(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current) -{ - // Attempt to set the runtime implementation for providing hosting services to the tracker runtime. - return (::InterlockedCompareExchangePointer( - &HostServices::RuntimeImpl, - objectHandle, current) == current); -} - HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* obj) { _ASSERTE(obj != nullptr); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 14859e4976dacc..84e62ad98ee73e 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -995,7 +995,6 @@ FCFuncStart(gComWrappersFuncs) QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl) QCFuncElement("GetOrCreateComInterfaceForObjectInternal", ComWrappersNative::GetOrCreateComInterfaceForObject) QCFuncElement("GetOrCreateObjectForComInstanceInternal", ComWrappersNative::GetOrCreateObjectForComInstance) - QCFuncElement("RegisterForReferenceTrackerHostInternal", ComWrappersNative::RegisterForReferenceTrackerHost) FCFuncEnd() #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 05c2e2273135ee..aa7e56f0855ea3 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -387,7 +387,6 @@ namespace // Defined handle types for the specific object uses. const HandleType InstanceHandleType{ HNDTYPE_STRONG }; - const HandleType ComWrappersImplHandleType{ HNDTYPE_STRONG }; void* CallComputeVTables( _In_ OBJECTREF* implPROTECTED, @@ -467,7 +466,7 @@ namespace } void* GetOrCreateComInterfaceForObjectInternal( - _In_ OBJECTREF impl, + _In_opt_ OBJECTREF impl, _In_ OBJECTREF instance, _In_ INT32 flags) { @@ -475,7 +474,6 @@ namespace { THROWS; MODE_COOPERATIVE; - PRECONDITION(impl != NULL); PRECONDITION(instance != NULL); POSTCONDITION(CheckPointer(RETVAL)); } @@ -571,7 +569,7 @@ namespace } OBJECTREF GetOrCreateObjectForComInstanceInternal( - _In_ OBJECTREF impl, + _In_opt_ OBJECTREF impl, _In_ IUnknown* identity, _In_ INT32 flags) { @@ -579,7 +577,6 @@ namespace { THROWS; MODE_COOPERATIVE; - PRECONDITION(impl != NULL); PRECONDITION(identity != NULL); POSTCONDITION(RETVAL != NULL); } @@ -782,7 +779,7 @@ namespace InteropLibImports return hr; } - HRESULT ReleaseExternalObjectsFromCurrentThread(_In_ InteropLib::OBJECTHANDLE handle) noexcept + HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept { CONTRACTL { @@ -805,7 +802,7 @@ namespace InteropLibImports ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectFromHandle(static_cast<::OBJECTHANDLE>(handle)); + gc.implRef = NULL; // Use the globally registered implementation. // Pass the objects along to get released. ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); @@ -864,7 +861,6 @@ namespace InteropLibImports } HRESULT GetOrCreateTrackerTargetForExternal( - _In_ InteropLib::OBJECTHANDLE impl, _In_ IUnknown* externalComObject, _In_ INT32 externalObjectFlags, _In_ INT32 trackerTargetFlags, @@ -874,7 +870,6 @@ namespace InteropLibImports { NOTHROW; MODE_PREEMPTIVE; - PRECONDITION(impl != NULL); PRECONDITION(externalComObject != NULL); PRECONDITION(trackerTarget != NULL); } @@ -895,7 +890,7 @@ namespace InteropLibImports ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - gc.implRef = ObjectFromHandle(static_cast<::OBJECTHANDLE>(impl)); + gc.implRef = NULL; // Use the globally registered implementation. // Get wrapper for external object gc.objRef = GetOrCreateObjectForComInstanceInternal( @@ -1060,40 +1055,6 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( END_QCALL; } -void QCALLTYPE ComWrappersNative::RegisterForReferenceTrackerHost( - _In_ QCall::ObjectHandleOnStack comWrappersImpl) -{ - QCALL_CONTRACT; - - OBJECTHANDLE implHandle; - - BEGIN_QCALL; - - // Enter cooperative mode to create the handle and store it - // for future use in the reference tracker host scenario. - { - GCX_COOP(); - - OBJECTREF implRef = NULL; - GCPROTECT_BEGIN(implRef); - - implRef = ObjectToOBJECTREF(*comWrappersImpl.m_ppObject); - _ASSERTE(implRef != NULL); - - implHandle = GetAppDomain()->CreateTypedHandle(implRef, ComWrappersImplHandleType); - - if (!InteropLib::Com::RegisterReferenceTrackerHostRuntimeImpl(implHandle)) - { - DestroyHandleCommon(implHandle, ComWrappersImplHandleType); - COMPlusThrow(kInvalidOperationException, IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS); - } - - GCPROTECT_END(); - } - - END_QCALL; -} - void QCALLTYPE ComWrappersNative::GetIUnknownImpl( _Out_ void** fpQueryInterface, _Out_ void** fpAddRef, diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 2d7c1804f9788a..89c519ede19bd9 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -28,9 +28,6 @@ class ComWrappersNative _In_ INT32 flags, _Inout_ QCall::ObjectHandleOnStack retValue); - static void QCALLTYPE RegisterForReferenceTrackerHost( - _In_ QCall::ObjectHandleOnStack comWrappersImpl); - public: // Lifetime management for COM Wrappers static void DestroyManagedObjectComWrapper(_In_ void* wrapper); static void DestroyExternalComObjectContext(_In_ void* context); From c00ebd29a3a8df2b024a415515f207f427760b37 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 11 Feb 2020 16:13:08 -0800 Subject: [PATCH 37/83] Remove unused message. --- src/coreclr/src/dlls/mscorrc/mscorrc.rc | 1 - src/coreclr/src/dlls/mscorrc/resource.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/coreclr/src/dlls/mscorrc/mscorrc.rc b/src/coreclr/src/dlls/mscorrc/mscorrc.rc index f7bd564e6909f2..8a7410ef25b2d5 100644 --- a/src/coreclr/src/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/src/dlls/mscorrc/mscorrc.rc @@ -579,7 +579,6 @@ BEGIN IDS_EE_ATTEMPT_TO_CREATE_GENERIC_CCW "Generic types cannot be marshaled to COM interface pointers." IDS_EE_ATTEMPT_TO_CREATE_NON_ABSTRACT_CCW "Types with non-abstract methods cannot be marshaled to COM interface pointers." IDS_EE_COMIMPORT_METHOD_NO_INTERFACE "Method '%1' in ComImport class '%2' must implement an interface method." - IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS "Attempt to update previously set default ReferenceTrackerHost callbacks." IDS_CLASSLOAD_BAD_METHOD_COUNT "Metadata method count does not match method enumeration length for type '%1' from assembly '%2'." IDS_CLASSLOAD_BAD_FIELD_COUNT "Metadata field count does not match field enumeration length for type '%1' from assembly '%2'." diff --git a/src/coreclr/src/dlls/mscorrc/resource.h b/src/coreclr/src/dlls/mscorrc/resource.h index 20261b06f58a45..342c0570d493bb 100644 --- a/src/coreclr/src/dlls/mscorrc/resource.h +++ b/src/coreclr/src/dlls/mscorrc/resource.h @@ -369,7 +369,6 @@ #define IDS_EE_COMIMPORT_METHOD_NO_INTERFACE 0x1aab #define IDS_EE_OUT_OF_MEMORY_WITHIN_RANGE 0x1aac #define IDS_EE_ARRAY_DIMENSIONS_EXCEEDED 0x1aad -#define IDS_EE_RESET_REFERENCETRACKERHOST_CALLBACKS 0x1aae #define IDS_CLASSLOAD_MI_CANNOT_OVERRIDE 0x1ab3 #define IDS_CLASSLOAD_COLLECTIBLEFIXEDVTATTR 0x1ab6 From 4eb1240738fd6497aa29c79326c8b3f0d8400da5 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 12 Feb 2020 11:17:12 -0800 Subject: [PATCH 38/83] Add error message to managed resources. Variable name feedback. --- .../System/Runtime/InteropServices/ComWrappers.cs | 12 ++++++------ .../src/Resources/Strings.resx | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 2a21e1b918de67..6d8281be651d88 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -104,7 +104,7 @@ private struct ComInterfaceInstance /// /// Globally registered instance of the ComWrappers class. /// - private static ComWrappers? s_GlobalInstance; + private static ComWrappers? s_globalInstance; /// /// Create an COM representation of the supplied object that can be passed to an non-managed environment. @@ -139,7 +139,7 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa // Call to execute the abstract instance function internal static unsafe void* CallComputeVtables(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) - => (comWrappersImpl ?? s_GlobalInstance!).ComputeVtables(obj, flags, out count); + => (comWrappersImpl ?? s_globalInstance!).ComputeVtables(obj, flags, out count); /// /// Get the currently registered managed object or creates a new managed object and registers it. @@ -176,7 +176,7 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb // Call to execute the abstract instance function internal static object CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) - => (comWrappersImpl ?? s_GlobalInstance!).CreateObject(externalComObject, agileObjectRef, flags); + => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags); /// /// Called when a request is made for a collection of objects to be released. @@ -192,7 +192,7 @@ protected virtual void ReleaseObjects(IEnumerable objects) // Call to execute the virtual instance function internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects) - => (comWrappersImpl ?? s_GlobalInstance!).ReleaseObjects(objects); + => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects); /// /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. @@ -203,9 +203,9 @@ internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerabl /// public void RegisterForReferenceTrackerHost() { - if (null != Interlocked.CompareExchange(ref s_GlobalInstance, this, null)) + if (null != Interlocked.CompareExchange(ref s_globalInstance, this, null)) { - throw new InvalidOperationException(); + throw new InvalidOperationException(SR.InvalidOperation_ResetReferenceTrackerHostCallbacks); } } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index d3e1a963ee6be1..518326b9b26bc5 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3652,6 +3652,9 @@ Type '{0}' has more than one COM unregistration function. + + Attempt to update previously set default ReferenceTrackerHost callbacks. + The callback populated its buffer with ill-formed UTF-8 data. Callbacks are required to populate the buffer only with well-formed UTF-8 data. From 61dc744578a51cd7a338106486d796dde3702f27 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 12 Feb 2020 17:25:37 -0800 Subject: [PATCH 39/83] Implement Peg/Unpeg --- src/coreclr/src/interop/comwrappers.cpp | 131 ++++++++++++------ src/coreclr/src/interop/comwrappers.h | 39 +++--- src/coreclr/src/interop/inc/interoplib.h | 29 ++-- .../src/interop/inc/interoplibimports.h | 4 +- src/coreclr/src/interop/interoplib.cpp | 27 ++-- .../src/interop/trackerobjectmanager.cpp | 4 +- src/coreclr/src/vm/interoplibinterface.cpp | 40 ++++-- 7 files changed, 167 insertions(+), 107 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index f67c35a0a81d0a..7eebede5a42415 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -217,11 +217,21 @@ namespace namespace { const int32_t TrackerRefShift = 32; - constexpr ULONGLONG TrackerRefCounter = ULONGLONG{ 1 } << TrackerRefShift; + const ULONGLONG TrackerRefCounter = ULONGLONG{ 1 } << TrackerRefShift; + const ULONGLONG ComRefCounter = ULONGLONG{ 1 }; + const ULONGLONG TrackerRefZero = 0x0000000080000000; + const ULONGLONG TrackerRefCountMask = 0xffffffff00000000; + const ULONGLONG ComRefCountMask = 0x000000007fffffff; + const ULONGLONG RefCountMask = 0xffffffff7fffffff; constexpr ULONG GetTrackerCount(_In_ ULONGLONG c) { - return static_cast(c >> TrackerRefShift); + return static_cast((c & TrackerRefCountMask) >> TrackerRefShift); + } + + constexpr ULONG GetComCount(_In_ ULONGLONG c) + { + return static_cast(c & ComRefCountMask); } ULONG STDMETHODCALLTYPE TrackerTarget_AddRefFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp) @@ -229,7 +239,7 @@ namespace _ASSERTE(disp != nullptr && disp->vtable != nullptr); ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport)); return wrapper->AddRefFromReferenceTracker(); } @@ -239,7 +249,7 @@ namespace _ASSERTE(disp != nullptr && disp->vtable != nullptr); ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport)); return wrapper->ReleaseFromReferenceTracker(); } @@ -249,7 +259,7 @@ namespace _ASSERTE(disp != nullptr && disp->vtable != nullptr); ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport)); return wrapper->Peg(); } @@ -259,7 +269,7 @@ namespace _ASSERTE(disp != nullptr && disp->vtable != nullptr); ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - _ASSERTE(wrapper->IsSet(CreateComInterfaceFlags::TrackerSupport)); + _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport)); return wrapper->Unpeg(); } @@ -312,7 +322,7 @@ ManagedObjectWrapper* ManagedObjectWrapper::MapFromIUnknown(_In_ IUnknown* pUnk) } HRESULT ManagedObjectWrapper::Create( - _In_ CreateComInterfaceFlags flags, + _In_ InteropLib::Com::CreateComInterfaceFlags flagsRaw, _In_ OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, _In_ ABI::ComInterfaceEntry* userDefined, @@ -320,12 +330,15 @@ HRESULT ManagedObjectWrapper::Create( { _ASSERTE(objectHandle != nullptr && mow != nullptr); + auto flags = static_cast(flagsRaw); + _ASSERTE((flags & CreateComInterfaceFlagsEx::InternalMask) == CreateComInterfaceFlagsEx::None); + // Maximum number of runtime supplied vtables. ABI::ComInterfaceEntry runtimeDefinedLocal[4]; int32_t runtimeDefinedCount = 0; // Check if the caller will provide the IUnknown table. - if ((flags & CreateComInterfaceFlags::CallerDefinedIUnknown) == CreateComInterfaceFlags::None) + if ((flags & CreateComInterfaceFlagsEx::CallerDefinedIUnknown) == CreateComInterfaceFlagsEx::None) { ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IUnknown); @@ -333,7 +346,7 @@ HRESULT ManagedObjectWrapper::Create( } // Check if the caller wants tracker support. - if ((flags & CreateComInterfaceFlags::TrackerSupport) == CreateComInterfaceFlags::TrackerSupport) + if ((flags & CreateComInterfaceFlagsEx::TrackerSupport) == CreateComInterfaceFlagsEx::TrackerSupport) { ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++]; curr.IID = __uuidof(IReferenceTrackerTarget); @@ -407,7 +420,7 @@ void ManagedObjectWrapper::Destroy(_In_ ManagedObjectWrapper* wrapper) } ManagedObjectWrapper::ManagedObjectWrapper( - _In_ CreateComInterfaceFlags flags, + _In_ CreateComInterfaceFlagsEx flags, _In_ OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, _In_ const ABI::ComInterfaceEntry* runtimeDefined, @@ -434,6 +447,45 @@ ManagedObjectWrapper::~ManagedObjectWrapper() _ASSERTE(Target == nullptr); } +ULONGLONG ManagedObjectWrapper::UniversalRelease(_In_ ULONGLONG dec) +{ + OBJECTHANDLE local = Target; + + LONGLONG refCount; + if (dec == ComRefCounter) + { + _ASSERTE(dec == 1); + refCount = ::InterlockedDecrement64(&_refCount); + } + else + { + _ASSERTE(dec == TrackerRefCounter); + LONGLONG prev; + do + { + prev = _refCount; + refCount = prev - dec; + } while (::InterlockedCompareExchange64(&_refCount, refCount, prev) != prev); + } + + // It is possible that a target wasn't set during an + // attempt to reactive the wrapper. + if ((RefCountMask & refCount) == 0 && local != nullptr) + { + _ASSERTE(!IsSet(CreateComInterfaceFlagsEx::IsPegged)); + _ASSERTE(refCount == TrackerRefZero || refCount == 0); + + // Attempt to reset the target if its current value is the same. + // It is possible the wrapper is in the middle of being reactivated. + (void)TrySetObjectHandle(nullptr, local); + + // Tell the runtime to delete the managed object instance handle. + InteropLibImports::DeleteObjectInstanceHandle(local); + } + + return refCount; +} + void* ManagedObjectWrapper::As(_In_ REFIID riid) { // Find target interface and return dispatcher or null if not found. @@ -461,9 +513,21 @@ bool ManagedObjectWrapper::TrySetObjectHandle(_In_ OBJECTHANDLE objectHandle, _I return (::InterlockedCompareExchangePointer(&Target, objectHandle, current) == current); } -bool ManagedObjectWrapper::IsSet(_In_ CreateComInterfaceFlags flag) const +bool ManagedObjectWrapper::IsSet(_In_ CreateComInterfaceFlagsEx flag) const +{ + return (_flags & flag) != CreateComInterfaceFlagsEx::None; +} + +void ManagedObjectWrapper::SetFlag(_In_ CreateComInterfaceFlagsEx flag) +{ + LONG setMask = (LONG)flag; + ::InterlockedOr((LONG*)&_flags, setMask); +} + +void ManagedObjectWrapper::ResetFlag(_In_ CreateComInterfaceFlagsEx flag) { - return (_flags & flag) != CreateComInterfaceFlags::None; + LONG resetMask = (LONG)~flag; + ::InterlockedAnd((LONG*)&_flags, resetMask); } ULONG ManagedObjectWrapper::AddRefFromReferenceTracker() @@ -481,27 +545,19 @@ ULONG ManagedObjectWrapper::AddRefFromReferenceTracker() ULONG ManagedObjectWrapper::ReleaseFromReferenceTracker() { - LONGLONG prev; - LONGLONG curr; - do - { - prev = _refCount; - curr = prev - TrackerRefCounter; - } while (::InterlockedCompareExchange64(&_refCount, curr, prev) != prev); - - return GetTrackerCount(curr); + return GetTrackerCount(UniversalRelease(TrackerRefCounter)); } HRESULT ManagedObjectWrapper::Peg() { - // [TODO] - return E_NOTIMPL; + SetFlag(CreateComInterfaceFlagsEx::IsPegged); + return S_OK; } HRESULT ManagedObjectWrapper::Unpeg() { - // [TODO] - return E_NOTIMPL; + ResetFlag(CreateComInterfaceFlagsEx::IsPegged); + return S_OK; } HRESULT ManagedObjectWrapper::QueryInterface( @@ -522,27 +578,12 @@ HRESULT ManagedObjectWrapper::QueryInterface( ULONG ManagedObjectWrapper::AddRef(void) { - return (ULONG)::InterlockedIncrement64(&_refCount); + return GetComCount(::InterlockedIncrement64(&_refCount)); } ULONG ManagedObjectWrapper::Release(void) { - OBJECTHANDLE local = Target; - ULONG refCount = (ULONG)::InterlockedDecrement64(&_refCount); - - // It is possible that a target wasn't set during an - // attempt to reactive the wrapper. - if (refCount == 0 && local != nullptr) - { - // Attempt to reset the target if its current value is the same. - // It is possible the wrapper is in the middle of being reactivated. - (void)TrySetObjectHandle(nullptr, local); - - // Tell the runtime to delete the managed object instance handle. - InteropLibImports::DeleteObjectInstanceHandle(local); - } - - return refCount; + return GetComCount(UniversalRelease(ComRefCounter)); } namespace @@ -569,7 +610,7 @@ NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_I HRESULT NativeObjectWrapperContext::Create( _In_ IUnknown* external, - _In_ CreateObjectFlags flags, + _In_ InteropLib::Com::CreateObjectFlags flags, _In_ size_t runtimeContextSize, _Outptr_ NativeObjectWrapperContext** context) { @@ -578,7 +619,7 @@ HRESULT NativeObjectWrapperContext::Create( HRESULT hr; ComHolder trackerObject; - if ((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject) + if (flags & InteropLib::Com::CreateObjectFlags_TrackerObject) { hr = external->QueryInterface(&trackerObject); if (SUCCEEDED(hr)) @@ -600,7 +641,7 @@ HRESULT NativeObjectWrapperContext::Create( if (trackerObject != nullptr) { // Inform the tracker object manager - _ASSERTE((flags & CreateObjectFlags::TrackerObject) == CreateObjectFlags::TrackerObject); + _ASSERTE(flags & InteropLib::Com::CreateObjectFlags_TrackerObject); hr = TrackerObjectManager::AfterWrapperCreated(trackerObject); if (FAILED(hr)) { diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 8205558d10716c..ea0380cfd54b74 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -9,24 +9,19 @@ #include #include "referencetrackertypes.h" -enum class CreateComInterfaceFlags +enum class CreateComInterfaceFlagsEx : LONG { - None = 0, - CallerDefinedIUnknown = 1, - TrackerSupport = 2, -}; + None = InteropLib::Com::CreateComInterfaceFlags_None, + CallerDefinedIUnknown = InteropLib::Com::CreateComInterfaceFlags_CallerDefinedIUnknown, + TrackerSupport = InteropLib::Com::CreateComInterfaceFlags_TrackerSupport, -DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlags); + // Highest bit is reserved for internal usage + IsPegged = 1 << 31, -enum class CreateObjectFlags -{ - None = 0, - TrackerObject = 1, - IgnoreCache = 2, + InternalMask = IsPegged, }; -DEFINE_ENUM_FLAG_OPERATORS(CreateObjectFlags); - +DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlagsEx); // Forward declarations namespace ABI @@ -39,7 +34,7 @@ namespace ABI class ManagedObjectWrapper { public: - InteropLib::OBJECTHANDLE Target; + Volatile Target; private: const int32_t _runtimeDefinedCount; @@ -49,7 +44,7 @@ class ManagedObjectWrapper ABI::ComInterfaceDispatch* _dispatches; LONGLONG _refCount; - const CreateComInterfaceFlags _flags; + Volatile _flags; public: // static // Get the implementation for IUnknown. @@ -64,7 +59,7 @@ class ManagedObjectWrapper // Create a ManagedObjectWrapper instance static HRESULT Create( - _In_ CreateComInterfaceFlags flags, + _In_ InteropLib::Com::CreateComInterfaceFlags flags, _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t userDefinedCount, _In_ ABI::ComInterfaceEntry* userDefined, @@ -75,7 +70,7 @@ class ManagedObjectWrapper private: ManagedObjectWrapper( - _In_ CreateComInterfaceFlags flags, + _In_ CreateComInterfaceFlagsEx flags, _In_ InteropLib::OBJECTHANDLE objectHandle, _In_ int32_t runtimeDefinedCount, _In_ const ABI::ComInterfaceEntry* runtimeDefined, @@ -85,12 +80,18 @@ class ManagedObjectWrapper ~ManagedObjectWrapper(); + // Represents a single implementation of how to release + // the wrapper. Supplied with a decrementing value. + ULONGLONG UniversalRelease(_In_ ULONGLONG dec); + public: void* As(_In_ REFIID riid); // Attempt to set the target object handle based on an assumed current value. bool TrySetObjectHandle(_In_ InteropLib::OBJECTHANDLE objectHandle, _In_ InteropLib::OBJECTHANDLE current = nullptr); - bool IsSet(_In_ CreateComInterfaceFlags flag) const; + bool IsSet(_In_ CreateComInterfaceFlagsEx flag) const; + void SetFlag(_In_ CreateComInterfaceFlagsEx flag); + void ResetFlag(_In_ CreateComInterfaceFlagsEx flag); public: // IReferenceTrackerTarget ULONG AddRefFromReferenceTracker(); @@ -127,7 +128,7 @@ class NativeObjectWrapperContext // Create a NativeObjectWrapperContext instance static HRESULT NativeObjectWrapperContext::Create( _In_ IUnknown* external, - _In_ CreateObjectFlags flags, + _In_ InteropLib::Com::CreateObjectFlags flags, _In_ size_t runtimeContextSize, _Outptr_ NativeObjectWrapperContext** context); diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index c9526c4f463c8d..f6f7ef885752b3 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -25,12 +25,20 @@ namespace InteropLib namespace Com { + // See CreateComInterfaceFlags in ComWrappers.cs + enum CreateComInterfaceFlags + { + CreateComInterfaceFlags_None = 0, + CreateComInterfaceFlags_CallerDefinedIUnknown = 1, + CreateComInterfaceFlags_TrackerSupport = 2, + }; + // Create an IUnknown instance that represents the supplied managed object instance. HRESULT CreateWrapperForObject( _In_ OBJECTHANDLE instance, _In_ INT32 vtableCount, _In_ void* vtables, - _In_ INT32 flags, + _In_ enum CreateComInterfaceFlags flags, _Outptr_ IUnknown** wrapper) noexcept; // Destroy the supplied wrapper @@ -44,17 +52,25 @@ namespace InteropLib // IAgileReference instance. IUnknown* AgileRef; - // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ for - // details. + // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ + // for details. bool FromTrackerRuntime; }; + // See CreateObjectFlags in ComWrappers.cs + enum CreateObjectFlags + { + CreateObjectFlags_None = 0, + CreateObjectFlags_TrackerObject = 1, + CreateObjectFlags_IgnoreCache = 2, + }; + // Allocate a wrapper context for an external object. // The runtime supplies the external object, flags, and a memory // request in order to bring the object into the runtime. HRESULT CreateWrapperForExternal( _In_ IUnknown* external, - _In_ INT32 flags, + _In_ enum CreateObjectFlags flags, _In_ size_t contextSize, _Out_ ExternalWrapperResult* result) noexcept; @@ -71,10 +87,7 @@ namespace InteropLib _Out_ void** fpRelease) noexcept; // Ensure the wrapper is active and take an AddRef. - // S_OK - the wrapper is active and the OBJECTHANDLE wasn't needed. - // S_FALSE - the wrapper was inactive and the OBJECTHANDLE argument was used. - // E_HANDLE - the supplied wrapper is inactive and an OBJECTHANDLE wasn't supplied. - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_opt_ OBJECTHANDLE handle) noexcept; + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; // Begin the reference tracking process on external COM objects. // This should only be called during a runtime's GC phase. diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 3c9dfdf103bc41..c2e79f046b7582 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -69,8 +69,8 @@ namespace InteropLibImports // external object. HRESULT GetOrCreateTrackerTargetForExternal( _In_ IUnknown* externalComObject, - _In_ INT32 externalObjectFlags, - _In_ INT32 trackerTargetFlags, + _In_ InteropLib::Com::CreateObjectFlags externalObjectFlags, + _In_ InteropLib::Com::CreateComInterfaceFlags trackerTargetFlags, _Outptr_ void** trackerTarget) noexcept; } diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index 923611e6456b94..d75e6673ba6c40 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -4,6 +4,7 @@ #include "platform.h" #include +#include #include "comwrappers.h" @@ -24,7 +25,7 @@ namespace InteropLib _In_ OBJECTHANDLE instance, _In_ INT32 vtableCount, _In_ void* vtablesRaw, - _In_ INT32 flagsRaw, + _In_ enum CreateComInterfaceFlags flags, _Outptr_ IUnknown** wrapper) noexcept { _ASSERTE(instance != nullptr && wrapper != nullptr); @@ -36,8 +37,7 @@ namespace InteropLib HRESULT hr; - // Convert inputs to appropriate types. - auto flags = static_cast(flagsRaw); + // Convert input to appropriate types. auto vtables = static_cast(vtablesRaw); ManagedObjectWrapper* mow; @@ -59,7 +59,7 @@ namespace InteropLib HRESULT CreateWrapperForExternal( _In_ IUnknown* external, - _In_ INT32 flagsRaw, + _In_ enum CreateObjectFlags flags, _In_ size_t contextSize, _Out_ ExternalWrapperResult* result) noexcept { @@ -71,9 +71,6 @@ namespace InteropLib ComHolder reference; RETURN_IF_FAILED(CreateAgileReference(external, &reference)); - // Convert input to appropriate type. - auto flags = static_cast(flagsRaw); - NativeObjectWrapperContext* wrapperContext; RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); @@ -122,24 +119,20 @@ namespace InteropLib ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); } - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_opt_ OBJECTHANDLE handle) noexcept + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept { ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); - if (wrapper == nullptr) + if (wrapper == nullptr || handle == nullptr) return E_INVALIDARG; ULONG count = wrapper->AddRef(); if (count == 1) { - // No object handle was supplied so reactivation isn't possible. - if (handle == nullptr) - { - wrapper->Release(); - return E_HANDLE; - } - ::InterlockedExchangePointer(&wrapper->Target, handle); - return S_FALSE; + } + else + { + InteropLibImports::DeleteObjectInstanceHandle(handle); } return S_OK; diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index df55b8df8abfb0..d4bfbe8dfca40d 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -127,8 +127,8 @@ namespace ComHolder target; RETURN_IF_FAILED(InteropLibImports::GetOrCreateTrackerTargetForExternal( identity, - (INT32)CreateObjectFlags::TrackerObject, - (INT32)CreateComInterfaceFlags::TrackerSupport, + InteropLib::Com::CreateObjectFlags_TrackerObject, + InteropLib::Com::CreateComInterfaceFlags_TrackerSupport, (void**)&target)); return target->QueryInterface(IID_IReferenceTrackerTarget, (void**)ppNewReference); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index aa7e56f0855ea3..122ad5b4b8ab98 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -14,6 +14,9 @@ #include "interoplibinterface.h" +using CreateObjectFlags = InteropLib::Com::CreateObjectFlags; +using CreateComInterfaceFlags = InteropLib::Com::CreateComInterfaceFlags; + namespace { // This class is used to track the external object within the runtime. @@ -468,7 +471,7 @@ namespace void* GetOrCreateComInterfaceForObjectInternal( _In_opt_ OBJECTREF impl, _In_ OBJECTREF instance, - _In_ INT32 flags) + _In_ CreateComInterfaceFlags flags) { CONTRACT(void*) { @@ -517,7 +520,12 @@ namespace OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); // Call the InteropLib and create the associated managed object wrapper. - hr = InteropLib::Com::CreateWrapperForObject(instHandle, vtableCount, vtables, flags, &newWrapper); + hr = InteropLib::Com::CreateWrapperForObject( + instHandle, + vtableCount, + vtables, + flags, + &newWrapper); if (FAILED(hr)) { DestroyHandleCommon(instHandle, InstanceHandleType); @@ -552,13 +560,9 @@ namespace { _ASSERTE(wrapper != NULL); // It is possible the supplied wrapper is no longer valid. If so, reactivate the - // wrapper with the object instance's new handle. If this reactivation - // wasn't needed, delete the handle. + // wrapper with the object instance's new handle. OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); hr = InteropLib::Com::EnsureActiveWrapperAndAddRef(static_cast(wrapper), instHandle); - if (hr != S_OK) - DestroyHandleCommon(instHandle, InstanceHandleType); - if (FAILED(hr)) COMPlusThrowHR(hr); } @@ -571,7 +575,7 @@ namespace OBJECTREF GetOrCreateObjectForComInstanceInternal( _In_opt_ OBJECTREF impl, _In_ IUnknown* identity, - _In_ INT32 flags) + _In_ CreateObjectFlags flags) { CONTRACT(OBJECTREF) { @@ -597,6 +601,9 @@ namespace ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); + // Check if the cache should be queried. + bool ignoreCache = !!(flags & CreateObjectFlags::CreateObjectFlags_IgnoreCache); + if (!ignoreCache) { // Query the external object cache ExtObjCxtCache::LockHolder lock(cache); @@ -635,14 +642,19 @@ namespace gc.objRef->GetSyncBlockIndex(), flags); - // Attempt to insert the new context into the cache. + if (ignoreCache) + { + extObjCxt = resultHolder.GetContext(); + } + else { + // Attempt to insert the new context into the cache. ExtObjCxtCache::LockHolder lock(cache); extObjCxt = cache->FindOrAdd(identity, resultHolder.GetContext()); } // If the returned context matches the new context it means the - // new context was inserted. + // new context was inserted or the cache is being ignored. if (extObjCxt == resultHolder.GetContext()) { // Update the object's SyncBlock with a handle to the context for runtime cleanup. @@ -862,8 +874,8 @@ namespace InteropLibImports HRESULT GetOrCreateTrackerTargetForExternal( _In_ IUnknown* externalComObject, - _In_ INT32 externalObjectFlags, - _In_ INT32 trackerTargetFlags, + _In_ CreateObjectFlags externalObjectFlags, + _In_ CreateComInterfaceFlags trackerTargetFlags, _Outptr_ void** trackerTarget) noexcept { CONTRACTL @@ -1010,7 +1022,7 @@ void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( wrapper = GetOrCreateComInterfaceForObjectInternal( ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), ObjectToOBJECTREF(*instance.m_ppObject), - flags); + (CreateComInterfaceFlags)flags); } END_QCALL; @@ -1046,7 +1058,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal( ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), identity, - flags); + (CreateObjectFlags)flags); // Set the return value retValue.Set(newObj); From 4d41a28b40005f117f5bb043a3159385e4e6923c Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 12 Feb 2020 17:25:59 -0800 Subject: [PATCH 40/83] Add additional testing. --- .../ReferenceTrackerRuntime.cpp | 187 ++++++++++++------ .../src/Interop/COM/ComWrappers/Program.cs | 60 ++++++ 2 files changed, 183 insertions(+), 64 deletions(-) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp index 06557b2e7f9a43..eaad73988bba5d 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace API @@ -60,66 +61,6 @@ namespace API namespace { - class TrackerRuntimeManagerImpl : public API::IReferenceTrackerManager - { - CComPtr runtimeServices; - public: - STDMETHOD(ReferenceTrackingStarted)() - { - // [TODO] - return S_OK; - } - - STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) - { - // [TODO] - return S_OK; - } - - STDMETHOD(ReferenceTrackingCompleted)() - { - // [TODO] - return S_OK; - } - - STDMETHOD(SetReferenceTrackerHost)(_In_ API::IReferenceTrackerHost* pHostServices) - { - assert(pHostServices != nullptr); - return pHostServices->QueryInterface(&runtimeServices); - } - - // Lifetime maintained by stack - we don't care about ref counts - STDMETHOD_(ULONG, AddRef)() { return 1; } - STDMETHOD_(ULONG, Release)() { return 1; } - - STDMETHOD(QueryInterface)( - /* [in] */ REFIID riid, - /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) - { - if (ppvObject == nullptr) - return E_POINTER; - - if (IsEqualIID(riid, __uuidof(API::IReferenceTrackerManager))) - { - *ppvObject = static_cast(this); - } - else if (IsEqualIID(riid, IID_IUnknown)) - { - *ppvObject = static_cast(this); - } - else - { - *ppvObject = nullptr; - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; - } - }; - - TrackerRuntimeManagerImpl TrackerRuntimeManager; - // Testing types struct DECLSPEC_UUID("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09") ITest : public IUnknown { @@ -143,6 +84,31 @@ namespace TrackerObject(size_t id) : _id{ id }, _trackerSourceCount{ 0 }, _connected{ false }, _elementId{ 1 } { } + HRESULT ToggleTargets(_In_ bool shouldPeg) + { + HRESULT hr; + + auto curr = std::begin(_elements); + while (curr != std::end(_elements)) + { + CComPtr mowMaybe; + if (S_OK == curr->second->QueryInterface(&mowMaybe)) + { + if (shouldPeg) + { + RETURN_IF_FAILED(mowMaybe->Peg()); + } + else + { + RETURN_IF_FAILED(mowMaybe->Unpeg()); + } + } + ++curr; + } + + return S_OK; + } + STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id) { assert(c != nullptr && id != nullptr); @@ -202,6 +168,92 @@ namespace DEFINE_REF_COUNTING() }; + std::atomic CurrentObjectId{}; + + class TrackerRuntimeManagerImpl : public API::IReferenceTrackerManager + { + CComPtr _runtimeServices; + std::list> _objects; + + public: + void RecordObject(_In_ TrackerObject* obj) + { + _objects.push_back(CComPtr{ obj }); + + if (_runtimeServices != nullptr) + _runtimeServices->AddMemoryPressure(sizeof(TrackerObject)); + } + + void ReleaseObjects() + { + size_t count = _objects.size(); + _objects.clear(); + if (_runtimeServices != nullptr) + _runtimeServices->RemoveMemoryPressure(sizeof(TrackerObject) * count); + } + + public: // IReferenceTrackerManager + STDMETHOD(ReferenceTrackingStarted)() + { + // Unpeg all instances + for (auto& i : _objects) + i->ToggleTargets(/* should peg */ false); + + return S_OK; + } + + STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) + { + // Verify and ensure all connected types are pegged + for (auto& i : _objects) + i->ToggleTargets(/* should peg */ true); + + return S_OK; + } + + STDMETHOD(ReferenceTrackingCompleted)() + { + return S_OK; + } + + STDMETHOD(SetReferenceTrackerHost)(_In_ API::IReferenceTrackerHost* pHostServices) + { + assert(pHostServices != nullptr); + return pHostServices->QueryInterface(&_runtimeServices); + } + + // Lifetime maintained by stack - we don't care about ref counts + STDMETHOD_(ULONG, AddRef)() { return 1; } + STDMETHOD_(ULONG, Release)() { return 1; } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + if (ppvObject == nullptr) + return E_POINTER; + + if (IsEqualIID(riid, __uuidof(API::IReferenceTrackerManager))) + { + *ppvObject = static_cast(this); + } + else if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + }; + + TrackerRuntimeManagerImpl TrackerRuntimeManager; + HRESULT STDMETHODCALLTYPE TrackerObject::ConnectFromTrackerSource() { _connected = true; @@ -219,7 +271,7 @@ namespace assert(pCallback != nullptr); CComPtr mowMaybe; - for (auto &e : _elements) + for (auto& e : _elements) { if (S_OK == e.second->QueryInterface(&mowMaybe)) { @@ -256,12 +308,19 @@ namespace /* Not used by runtime */ return E_NOTIMPL; } - - std::atomic CurrentObjectId{}; } // Create external object extern "C" DLL_EXPORT ITrackerObject* STDMETHODCALLTYPE CreateTrackerObject() { - return new TrackerObject{ CurrentObjectId++ }; + auto obj = new TrackerObject{ CurrentObjectId++ }; + + TrackerRuntimeManager.RecordObject(obj); + + return obj; +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReleaseAllTrackerObjects() +{ + TrackerRuntimeManager.ReleaseObjects(); } diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 5770ee63a39227..66ea2eeeebfba0 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -5,6 +5,7 @@ namespace ComWrappersTests { using System; + using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -24,7 +25,12 @@ interface ITest class Test : ITest { + public static int InstanceCount = 0; + private int value = -1; + public Test() { InstanceCount++; } + ~Test() { InstanceCount--; } + public void SetValue(int i) => this.value = i; public int GetValue() => this.value; } @@ -68,6 +74,9 @@ struct MockReferenceTrackerRuntime { [DllImport(nameof(MockReferenceTrackerRuntime))] extern public static IntPtr CreateTrackerObject(); + + [DllImport(nameof(MockReferenceTrackerRuntime))] + extern public static void ReleaseAllTrackerObjects(); } [Guid("42951130-245C-485E-B60B-4ED4254256F8")] @@ -227,6 +236,56 @@ static void ValidateComInterfaceCreation() Assert.AreEqual(count, 0); } + static void ValidateRuntimeTrackerScenario() + { + Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}..."); + + var cw = new MyComWrappers(); + + // Get an object from a tracker runtime. + IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); + + // Create a managed wrapper for the native object. + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + + var testWrapperIds = new List(); + for (int i = 0; i < 1000; ++i) + { + // Create a native wrapper for the managed object. + IntPtr testWrapper = cw.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.TrackerSupport); + + // Pass the managed object to the native object. + int id = trackerObj.AddObjectRef(testWrapper); + + // Retain the managed object wrapper ptr. + testWrapperIds.Add(id); + } + + Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); + + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + + Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); + + // Remove the managed object ref from the native object. + foreach (int id in testWrapperIds) + { + trackerObj.DropObjectRef(id); + } + + testWrapperIds.Clear(); + + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + } + static void ValidateIUnknownImpls() => MyComWrappers.ValidateIUnknownImpls(); @@ -256,6 +315,7 @@ static int Main(string[] doNotUse) try { ValidateComInterfaceCreation(); + ValidateRuntimeTrackerScenario(); ValidateIUnknownImpls(); ValidateRegisterForReferenceTrackerHost(); } From 241e204b7cfdf490e376c4a3cf7c558488e1f2f6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 12 Feb 2020 18:13:11 -0800 Subject: [PATCH 41/83] Test ignore cache logic. --- src/coreclr/src/vm/interoplibinterface.cpp | 2 +- .../src/Interop/COM/ComWrappers/Program.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 122ad5b4b8ab98..9e4cb5d2280a1a 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -587,7 +587,7 @@ namespace CONTRACT_END; HRESULT hr; - ExternalObjectContext* extObjCxt; + ExternalObjectContext* extObjCxt = NULL; struct { diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 66ea2eeeebfba0..0b6e20fd080346 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -286,6 +286,23 @@ static void ValidateRuntimeTrackerScenario() GC.Collect(); } + static void ValidateCreateObjectCachingScenario() + { + Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}..."); + + var cw = new MyComWrappers(); + + // Get an object from a tracker runtime. + IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); + + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + Assert.AreEqual(trackerObj1, trackerObj2); + + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.IgnoreCache); + Assert.AreNotEqual(trackerObj1, trackerObj3); + } + static void ValidateIUnknownImpls() => MyComWrappers.ValidateIUnknownImpls(); @@ -316,6 +333,7 @@ static int Main(string[] doNotUse) { ValidateComInterfaceCreation(); ValidateRuntimeTrackerScenario(); + ValidateCreateObjectCachingScenario(); ValidateIUnknownImpls(); ValidateRegisterForReferenceTrackerHost(); } From aeb94032f0121ae722488627cea122e535d33595 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 12 Feb 2020 22:14:48 -0800 Subject: [PATCH 42/83] Change EnsureActiveWrapperAndAddRef() API to take an indirect OBJECTREF and only create an OBJECTHANDLE if needed. --- src/coreclr/src/interop/inc/interoplib.h | 5 ++- .../src/interop/inc/interoplibimports.h | 10 +++++- src/coreclr/src/interop/interoplib.cpp | 17 ++++++---- src/coreclr/src/vm/interoplibinterface.cpp | 33 +++++++++++++++++-- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index f6f7ef885752b3..e570fac396940f 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -17,6 +17,7 @@ namespace InteropLibImports namespace InteropLib { using OBJECTHANDLE = void*; + using OBJECTREF_PROTECTED = void*; // Clean up the interop library. void Shutdown() noexcept; @@ -87,7 +88,9 @@ namespace InteropLib _Out_ void** fpRelease) noexcept; // Ensure the wrapper is active and take an AddRef. - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapper, _In_ OBJECTHANDLE handle) noexcept; + HRESULT EnsureActiveWrapperAndAddRef( + _In_ IUnknown* wrapper, + _In_ OBJECTREF_PROTECTED objRef) noexcept; // Begin the reference tracking process on external COM objects. // This should only be called during a runtime's GC phase. diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index c2e79f046b7582..a151e639aa8f69 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -42,7 +42,15 @@ namespace InteropLibImports // Release objects associated with the current thread. HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept; - // Delete Object instance handle + // Create Object instance handle from a protected OBJECTREF. + // N.B. A protected OBJECTREF provides a level of indirection that permits + // the passing of the OBJECTREF around, but passing around without + // creating a GC hole. + HRESULT CreateObjectInstanceHandle( + _In_ InteropLib::OBJECTREF_PROTECTED objRefProtected, + _Out_ InteropLib::OBJECTHANDLE *handle) noexcept; + + // Delete Object instance handle. void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; // Get the current global pegging state. diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index d75e6673ba6c40..b7c7d77ed7c884 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -9,6 +9,7 @@ #include "comwrappers.h" using OBJECTHANDLE = InteropLib::OBJECTHANDLE; +using OBJECTREF_PROTECTED = InteropLib::OBJECTREF_PROTECTED; using RuntimeCallContext = InteropLibImports::RuntimeCallContext; namespace InteropLib @@ -119,21 +120,25 @@ namespace InteropLib ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); } - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept + HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTREF_PROTECTED objRef) noexcept { ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); - if (wrapper == nullptr || handle == nullptr) + if (wrapper == nullptr) return E_INVALIDARG; ULONG count = wrapper->AddRef(); if (count == 1) { + OBJECTHANDLE handle; + HRESULT hr = InteropLibImports::CreateObjectInstanceHandle(objRef, &handle); + if (FAILED(hr)) + { + (void)wrapper->Release(); + return hr; + } + ::InterlockedExchangePointer(&wrapper->Target, handle); } - else - { - InteropLibImports::DeleteObjectInstanceHandle(handle); - } return S_OK; } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 9e4cb5d2280a1a..3e728ac3d491a9 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -560,9 +560,10 @@ namespace { _ASSERTE(wrapper != NULL); // It is possible the supplied wrapper is no longer valid. If so, reactivate the - // wrapper with the object instance's new handle. - OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); - hr = InteropLib::Com::EnsureActiveWrapperAndAddRef(static_cast(wrapper), instHandle); + // wrapper using the protected OBJECTREF. + hr = InteropLib::Com::EnsureActiveWrapperAndAddRef( + static_cast(wrapper), + static_cast(&gc.instRef)); if (FAILED(hr)) COMPlusThrowHR(hr); } @@ -831,6 +832,32 @@ namespace InteropLibImports return hr; } + HRESULT CreateObjectInstanceHandle( + _In_ InteropLib::OBJECTREF_PROTECTED objRefProtected, + _Out_ InteropLib::OBJECTHANDLE *handle) noexcept + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(objRefProtected != NULL); + PRECONDITION(handle != NULL); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + EX_TRY + { + OBJECTREF* objRef = static_cast(objRefProtected); + OBJECTHANDLE h = GetAppDomain()->CreateTypedHandle(*objRef, InstanceHandleType); + *handle = static_cast(h); + } + EX_CATCH_HRESULT(hr); + + return hr; + } + void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept { CONTRACTL From 362ea4ba933c0431c685c2c8039da3ee9777c640 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 13 Feb 2020 11:08:09 -0800 Subject: [PATCH 43/83] Fix Release builds --- src/coreclr/src/vm/interoplibinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 3e728ac3d491a9..43a6e6a6e002a6 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -1140,7 +1140,7 @@ void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) _ASSERTE(!context->IsActive()); #endif - InteropLib::Com::DestroyWrapperForExternal(context); + InteropLib::Com::DestroyWrapperForExternal(contextRaw); } void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* contextRaw) From 4d4d3953f68977216af51c6342d7cba8899844e6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 13 Feb 2020 16:10:50 -0800 Subject: [PATCH 44/83] Apply review feedback about active wrapper API. --- src/coreclr/src/interop/comwrappers.cpp | 12 ++++ src/coreclr/src/interop/comwrappers.h | 4 +- src/coreclr/src/interop/inc/interoplib.h | 12 ++-- .../src/interop/inc/interoplibimports.h | 8 --- src/coreclr/src/interop/interoplib.cpp | 58 +++++++++++-------- src/coreclr/src/vm/interoplibinterface.cpp | 52 +++++------------ 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 7eebede5a42415..c238eee04c4d63 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -530,6 +530,18 @@ void ManagedObjectWrapper::ResetFlag(_In_ CreateComInterfaceFlagsEx flag) ::InterlockedAnd((LONG*)&_flags, resetMask); } +ULONG ManagedObjectWrapper::IsActiveAddRef() +{ + ULONG count = GetComCount(::InterlockedIncrement64(&_refCount)); + if (count == 1) + { + // Ensure the current target is null. + ::InterlockedExchangePointer(&Target, nullptr); + } + + return count; +} + ULONG ManagedObjectWrapper::AddRefFromReferenceTracker() { LONGLONG prev; diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index ea0380cfd54b74..5a8abce729e02c 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -85,7 +85,6 @@ class ManagedObjectWrapper ULONGLONG UniversalRelease(_In_ ULONGLONG dec); public: - void* As(_In_ REFIID riid); // Attempt to set the target object handle based on an assumed current value. bool TrySetObjectHandle(_In_ InteropLib::OBJECTHANDLE objectHandle, _In_ InteropLib::OBJECTHANDLE current = nullptr); @@ -93,6 +92,9 @@ class ManagedObjectWrapper void SetFlag(_In_ CreateComInterfaceFlagsEx flag); void ResetFlag(_In_ CreateComInterfaceFlagsEx flag); + // Used while validating wrapper is active. + ULONG IsActiveAddRef(); + public: // IReferenceTrackerTarget ULONG AddRefFromReferenceTracker(); ULONG ReleaseFromReferenceTracker(); diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index e570fac396940f..3d9b988297b478 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -17,7 +17,6 @@ namespace InteropLibImports namespace InteropLib { using OBJECTHANDLE = void*; - using OBJECTREF_PROTECTED = void*; // Clean up the interop library. void Shutdown() noexcept; @@ -45,6 +44,12 @@ namespace InteropLib // Destroy the supplied wrapper void DestroyWrapperForObject(_In_ void* wrapper) noexcept; + // Check if a wrapper is active. + HRESULT IsActiveWrapper(_In_ IUnknown* wrapper) noexcept; + + // Reactivate the supplied wrapper. + HRESULT ReactivateWrapper(_In_ IUnknown* wrapper, _In_ InteropLib::OBJECTHANDLE handle) noexcept; + struct ExternalWrapperResult { // The returned context memory is guaranteed to be initialized to zero. @@ -87,11 +92,6 @@ namespace InteropLib _Out_ void** fpAddRef, _Out_ void** fpRelease) noexcept; - // Ensure the wrapper is active and take an AddRef. - HRESULT EnsureActiveWrapperAndAddRef( - _In_ IUnknown* wrapper, - _In_ OBJECTREF_PROTECTED objRef) noexcept; - // Begin the reference tracking process on external COM objects. // This should only be called during a runtime's GC phase. HRESULT BeginExternalObjectReferenceTracking(_In_ InteropLibImports::RuntimeCallContext* cxt) noexcept; diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index a151e639aa8f69..9d81a2fb625c32 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -42,14 +42,6 @@ namespace InteropLibImports // Release objects associated with the current thread. HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept; - // Create Object instance handle from a protected OBJECTREF. - // N.B. A protected OBJECTREF provides a level of indirection that permits - // the passing of the OBJECTREF around, but passing around without - // creating a GC hole. - HRESULT CreateObjectInstanceHandle( - _In_ InteropLib::OBJECTREF_PROTECTED objRefProtected, - _Out_ InteropLib::OBJECTHANDLE *handle) noexcept; - // Delete Object instance handle. void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept; diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index b7c7d77ed7c884..50d1f2dedc7f5e 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -9,7 +9,6 @@ #include "comwrappers.h" using OBJECTHANDLE = InteropLib::OBJECTHANDLE; -using OBJECTREF_PROTECTED = InteropLib::OBJECTREF_PROTECTED; using RuntimeCallContext = InteropLibImports::RuntimeCallContext; namespace InteropLib @@ -58,6 +57,40 @@ namespace InteropLib ManagedObjectWrapper::Destroy(wrapper); } + HRESULT IsActiveWrapper(_In_ IUnknown* wrapperMaybe) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); + if (wrapper == nullptr) + return E_INVALIDARG; + + ULONG count = wrapper->IsActiveAddRef(); + if (count == 1 || wrapper->Target == nullptr) + { + // The wrapper isn't active. + (void)wrapper->Release(); + return S_FALSE; + } + + return S_OK; + } + + HRESULT ReactivateWrapper(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept + { + ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); + if (wrapper == nullptr || handle == nullptr) + return E_INVALIDARG; + + // Take an AddRef() as an indication of ownership. + (void)wrapper->AddRef(); + + // If setting this object handle fails, then the race + // was lost and we will cleanup the handle. + if (!wrapper->TrySetObjectHandle(handle)) + InteropLibImports::DeleteObjectInstanceHandle(handle); + + return S_OK; + } + HRESULT CreateWrapperForExternal( _In_ IUnknown* external, _In_ enum CreateObjectFlags flags, @@ -120,29 +153,6 @@ namespace InteropLib ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease); } - HRESULT EnsureActiveWrapperAndAddRef(_In_ IUnknown* wrapperMaybe, _In_ OBJECTREF_PROTECTED objRef) noexcept - { - ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe); - if (wrapper == nullptr) - return E_INVALIDARG; - - ULONG count = wrapper->AddRef(); - if (count == 1) - { - OBJECTHANDLE handle; - HRESULT hr = InteropLibImports::CreateObjectInstanceHandle(objRef, &handle); - if (FAILED(hr)) - { - (void)wrapper->Release(); - return hr; - } - - ::InterlockedExchangePointer(&wrapper->Target, handle); - } - - return S_OK; - } - HRESULT BeginExternalObjectReferenceTracking(_In_ RuntimeCallContext* cxt) noexcept { return TrackerObjectManager::BeginReferenceTracking(cxt); diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 43a6e6a6e002a6..65562223aea980 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -485,7 +485,7 @@ namespace HRESULT hr; SafeComHolder newWrapper; - void* wrapper = NULL; + void* wrapperRaw = NULL; struct { @@ -504,7 +504,7 @@ namespace _ASSERTE(syncBlock->IsPrecious()); // Query the associated InteropSyncBlockInfo for an existing managed object wrapper. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw)) { // Compute VTables for the new existing COM object using the supplied COM Wrappers implementation. // @@ -515,7 +515,7 @@ namespace void* vtables = CallComputeVTables(&gc.implRef, &gc.instRef, flags, &vtableCount); // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw)) { OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); @@ -541,7 +541,7 @@ namespace // If the managed object wrapper couldn't be set, then // it should be possible to get the current one. - if (!interopInfo->TryGetManagedObjectComWrapper(&wrapper)) + if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw)) { UNREACHABLE(); } @@ -554,23 +554,29 @@ namespace { // A new managed object wrapper was created, remove the object from the holder. // No AddRef() here since the wrapper should be created with a reference. - wrapper = newWrapper.Extract(); + wrapperRaw = newWrapper.Extract(); } else { - _ASSERTE(wrapper != NULL); + _ASSERTE(wrapperRaw != NULL); + // It is possible the supplied wrapper is no longer valid. If so, reactivate the // wrapper using the protected OBJECTREF. - hr = InteropLib::Com::EnsureActiveWrapperAndAddRef( - static_cast(wrapper), - static_cast(&gc.instRef)); + IUnknown* wrapper = static_cast(wrapperRaw); + hr = InteropLib::Com::IsActiveWrapper(wrapper); + if (hr == S_FALSE) + { + OBJECTHANDLE h = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); + hr = InteropLib::Com::ReactivateWrapper(wrapper, static_cast(h)); + } + if (FAILED(hr)) COMPlusThrowHR(hr); } GCPROTECT_END(); - RETURN wrapper; + RETURN wrapperRaw; } OBJECTREF GetOrCreateObjectForComInstanceInternal( @@ -832,32 +838,6 @@ namespace InteropLibImports return hr; } - HRESULT CreateObjectInstanceHandle( - _In_ InteropLib::OBJECTREF_PROTECTED objRefProtected, - _Out_ InteropLib::OBJECTHANDLE *handle) noexcept - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - PRECONDITION(objRefProtected != NULL); - PRECONDITION(handle != NULL); - } - CONTRACTL_END; - - HRESULT hr = S_OK; - EX_TRY - { - OBJECTREF* objRef = static_cast(objRefProtected); - OBJECTHANDLE h = GetAppDomain()->CreateTypedHandle(*objRef, InstanceHandleType); - *handle = static_cast(h); - } - EX_CATCH_HRESULT(hr); - - return hr; - } - void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept { CONTRACTL From 5a9cbb40fbf8607be313fbec2e77838acf8f0167 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 13 Feb 2020 22:00:58 -0800 Subject: [PATCH 45/83] Update src/coreclr/src/interop/comwrappers.h Co-Authored-By: Jan Kotas --- src/coreclr/src/interop/comwrappers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.h index 5a8abce729e02c..75c3f9893da767 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.h @@ -9,7 +9,7 @@ #include #include "referencetrackertypes.h" -enum class CreateComInterfaceFlagsEx : LONG +enum class CreateComInterfaceFlagsEx : int32_t { None = InteropLib::Com::CreateComInterfaceFlags_None, CallerDefinedIUnknown = InteropLib::Com::CreateComInterfaceFlags_CallerDefinedIUnknown, @@ -261,4 +261,4 @@ struct ComHolder } } }; -#endif // _INTEROP_COMWRAPPERS_H_ \ No newline at end of file +#endif // _INTEROP_COMWRAPPERS_H_ From 48a14802c6c6b8a0af6143fcde40ca40b5f27797 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 14 Feb 2020 11:50:36 -0800 Subject: [PATCH 46/83] Bug in ignore cache scenario - found during local GCStress run. --- src/coreclr/src/vm/interoplibinterface.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 65562223aea980..96064116da7b67 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -33,6 +33,7 @@ namespace Flags_None = 0, Flags_Collected = 1, Flags_ReferenceTracker = 2, + Flags_InCache = 4, }; DWORD Flags; @@ -639,9 +640,12 @@ namespace COMPlusThrow(kArgumentNullException); // Construct the new context with the object details. - DWORD flags = resultHolder.Result.FromTrackerRuntime + DWORD flags = (resultHolder.Result.FromTrackerRuntime ? ExternalObjectContext::Flags_ReferenceTracker - : ExternalObjectContext::Flags_None; + : ExternalObjectContext::Flags_None) | + (ignoreCache + ? ExternalObjectContext::Flags_None + : ExternalObjectContext::Flags_InCache); ExternalObjectContext::Construct( resultHolder.GetContext(), identity, @@ -1139,8 +1143,12 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context _ASSERTE(context->IsActive()); context->MarkCollected(); - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); - cache->Remove(context); + // Verify the caller didn't ignore the cache during creation. + if (context->IsSet(ExternalObjectContext::Flags_InCache)) + { + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); + cache->Remove(context); + } } #endif // FEATURE_COMINTEROP From 4bea8da24297b71e61438996968c5e2a8ee6884a Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 14 Feb 2020 11:51:15 -0800 Subject: [PATCH 47/83] Finish off minimal testing scenarios. --- .../ReferenceTrackerRuntime.cpp | 14 ++ .../src/Interop/COM/ComWrappers/Program.cs | 165 ++++++++++++++++-- 2 files changed, 169 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp index eaad73988bba5d..50580d71da4bc8 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -192,6 +192,14 @@ namespace _runtimeServices->RemoveMemoryPressure(sizeof(TrackerObject) * count); } + HRESULT NotifyEndOfReferenceTrackingOnThread() + { + if (_runtimeServices != nullptr) + return _runtimeServices->NotifyEndOfReferenceTrackingOnThread(); + + return S_OK; + } + public: // IReferenceTrackerManager STDMETHOD(ReferenceTrackingStarted)() { @@ -320,7 +328,13 @@ extern "C" DLL_EXPORT ITrackerObject* STDMETHODCALLTYPE CreateTrackerObject() return obj; } +// Release the reference on all internally held tracker objects extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReleaseAllTrackerObjects() { TrackerRuntimeManager.ReleaseObjects(); } + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Trigger_NotifyEndOfReferenceTrackingOnThread() +{ + return TrackerRuntimeManager.NotifyEndOfReferenceTrackingOnThread(); +} diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 0b6e20fd080346..cfe32d41f2402b 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -5,6 +5,7 @@ namespace ComWrappersTests { using System; + using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; @@ -77,6 +78,9 @@ struct MockReferenceTrackerRuntime [DllImport(nameof(MockReferenceTrackerRuntime))] extern public static void ReleaseAllTrackerObjects(); + + [DllImport(nameof(MockReferenceTrackerRuntime))] + extern public static int Trigger_NotifyEndOfReferenceTrackingOnThread(); } [Guid("42951130-245C-485E-B60B-4ED4254256F8")] @@ -147,8 +151,20 @@ public void DropObjectRef(int id) } } - class MyComWrappers : ComWrappers + class TestComWrappers : ComWrappers { + public static readonly TestComWrappers Global = new TestComWrappers(); + + public TestComWrappers() + { + DefaultReleaseObjects = true; + } + + public bool DefaultReleaseObjects + { + get; set; + } + protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { Assert.IsTrue(obj is Test); @@ -191,6 +207,15 @@ protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj return new ITrackerObjectWrapper(iTestComObject); } + protected override void ReleaseObjects(IEnumerable objects) + { + if (DefaultReleaseObjects) + { + base.ReleaseObjects(objects); + Assert.Fail("Default implementation should throw exception"); + } + } + public static void ValidateIUnknownImpls() { Console.WriteLine($"Running {nameof(ValidateIUnknownImpls)}..."); @@ -209,7 +234,7 @@ static void ValidateComInterfaceCreation() var testObj = new Test(); - var wrappers = new MyComWrappers(); + var wrappers = new TestComWrappers(); // Allocate a wrapper for the object IntPtr comWrapper = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); @@ -240,7 +265,7 @@ static void ValidateRuntimeTrackerScenario() { Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}..."); - var cw = new MyComWrappers(); + var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); @@ -248,6 +273,9 @@ static void ValidateRuntimeTrackerScenario() // Create a managed wrapper for the native object. var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + // Ownership has been transferred to the wrapper. + Marshal.Release(trackerObjRaw); + var testWrapperIds = new List(); for (int i = 0; i < 1000; ++i) { @@ -290,7 +318,7 @@ static void ValidateCreateObjectCachingScenario() { Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}..."); - var cw = new MyComWrappers(); + var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); @@ -299,18 +327,22 @@ static void ValidateCreateObjectCachingScenario() var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreEqual(trackerObj1, trackerObj2); + // Ownership has been transferred to the wrapper. + Marshal.Release(trackerObjRaw); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.IgnoreCache); Assert.AreNotEqual(trackerObj1, trackerObj3); } static void ValidateIUnknownImpls() - => MyComWrappers.ValidateIUnknownImpls(); + => TestComWrappers.ValidateIUnknownImpls(); - static void ValidateRegisterForReferenceTrackerHost() + static void ValidateGlobalInstanceScenarios() { - Console.WriteLine($"Running {nameof(ValidateRegisterForReferenceTrackerHost)}..."); + Console.WriteLine($"Running {nameof(ValidateGlobalInstanceScenarios)}..."); + Console.WriteLine($"Validate RegisterForReferenceTrackerHost()..."); - var wrappers1 = new MyComWrappers(); + var wrappers1 = TestComWrappers.Global; wrappers1.RegisterForReferenceTrackerHost(); Assert.Throws( @@ -319,12 +351,120 @@ static void ValidateRegisterForReferenceTrackerHost() wrappers1.RegisterForReferenceTrackerHost(); }, "Should not be able to re-register for ReferenceTrackerHost"); - var wrappers2 = new MyComWrappers(); + var wrappers2 = new TestComWrappers(); Assert.Throws( () => { wrappers2.RegisterForReferenceTrackerHost(); }, "Should not be able to reset for ReferenceTrackerHost"); + + Console.WriteLine($"Validate NotifyEndOfReferenceTrackingOnThread()..."); + + int hr; + var cw = TestComWrappers.Global; + + // Trigger the thread lifetime end API and verify the default behavior. + hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); + Assert.AreEqual(unchecked((int)0x80004001), hr); + + cw.DefaultReleaseObjects = false; + // Trigger the thread lifetime end API and verify the customizable behavior. + hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); + Assert.AreEqual(0, hr); + + // Reset the global wrapper state. + cw.DefaultReleaseObjects = true; + } + + class BadComWrappers : ComWrappers + { + public enum FailureMode + { + ReturnInvalid, + ThrowException, + } + + public const int ExceptionErrorCode = 0x27; + + public FailureMode ComputeVtablesMode { get; set; } + public FailureMode CreateObjectMode { get; set; } + + protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + switch (ComputeVtablesMode) + { + case FailureMode.ReturnInvalid: + { + count = -1; + return null; + } + case FailureMode.ThrowException: + throw new Exception() { HResult = ExceptionErrorCode }; + default: + Assert.Fail("Invalid failure mode"); + throw new Exception("UNREACHABLE"); + } + + } + + protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + { + switch (CreateObjectMode) + { + case FailureMode.ReturnInvalid: + return null; + case FailureMode.ThrowException: + throw new Exception() { HResult = ExceptionErrorCode }; + default: + Assert.Fail("Invalid failure mode"); + throw new Exception("UNREACHABLE"); + } + } + } + + static void ValidateBadComWrapperImpl() + { + Console.WriteLine($"Running {nameof(ValidateBadComWrapperImpl)}..."); + + var wrapper = new BadComWrappers(); + + Assert.Throws( + () => + { + wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ReturnInvalid; + wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None); + }); + + try + { + wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ThrowException; + wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None); + } + catch (Exception e) + { + Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult); + } + + IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); + + Assert.Throws( + () => + { + wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + }); + + try + { + wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + } + catch (Exception e) + { + Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult); + } + + Marshal.Release(trackerObjRaw); } static int Main(string[] doNotUse) @@ -335,7 +475,11 @@ static int Main(string[] doNotUse) ValidateRuntimeTrackerScenario(); ValidateCreateObjectCachingScenario(); ValidateIUnknownImpls(); - ValidateRegisterForReferenceTrackerHost(); + ValidateBadComWrapperImpl(); + + // Perform all global impacting test scenarios last to + // avoid polluting non-global tests. + ValidateGlobalInstanceScenarios(); } catch (Exception e) { @@ -347,3 +491,4 @@ static int Main(string[] doNotUse) } } } + From 1bfea8c0b7a908dfe0f9a9317e148f5d5c74e86f Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 14 Feb 2020 12:34:26 -0800 Subject: [PATCH 48/83] Fix build issues on macOS using clang. --- src/coreclr/src/interop/CMakeLists.txt | 4 ++-- src/coreclr/src/interop/agilereferences.cpp | 2 +- src/coreclr/src/interop/comwrappers.cpp | 2 +- .../src/interop/{comwrappers.h => comwrappers.hpp} | 2 +- src/coreclr/src/interop/inc/interoplib.h | 4 ---- src/coreclr/src/interop/interoplib.cpp | 10 +++++++++- src/coreclr/src/interop/platform.h | 8 +++++--- ...ferencetrackertypes.h => referencetrackertypes.hpp} | 0 src/coreclr/src/interop/trackerobjectmanager.cpp | 2 +- 9 files changed, 20 insertions(+), 14 deletions(-) rename src/coreclr/src/interop/{comwrappers.h => comwrappers.hpp} (99%) rename src/coreclr/src/interop/{referencetrackertypes.h => referencetrackertypes.hpp} (100%) diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index af5f448d297857..47a3e5970e523e 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -24,9 +24,9 @@ if (WIN32) ${INTEROP_HEADERS} agilereferences.cpp comwrappers.cpp - comwrappers.h + comwrappers.hpp trackerobjectmanager.cpp - referencetrackertypes.h) + referencetrackertypes.hpp) endif(WIN32) convert_to_absolute_path(INTEROP_SOURCES ${INTEROP_SOURCES}) diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp index 2971766b0a2a81..58937ba2a0f307 100644 --- a/src/coreclr/src/interop/agilereferences.cpp +++ b/src/coreclr/src/interop/agilereferences.cpp @@ -5,7 +5,7 @@ // Runtime headers #include -#include "comwrappers.h" +#include "comwrappers.hpp" // Forward declare the Win32 API. enum AgileReferenceOptions diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index c238eee04c4d63..427df083d6672b 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include "comwrappers.h" +#include "comwrappers.hpp" #include using OBJECTHANDLE = InteropLib::OBJECTHANDLE; diff --git a/src/coreclr/src/interop/comwrappers.h b/src/coreclr/src/interop/comwrappers.hpp similarity index 99% rename from src/coreclr/src/interop/comwrappers.h rename to src/coreclr/src/interop/comwrappers.hpp index 75c3f9893da767..2fe09314c2efd3 100644 --- a/src/coreclr/src/interop/comwrappers.h +++ b/src/coreclr/src/interop/comwrappers.hpp @@ -7,7 +7,7 @@ #include "platform.h" #include -#include "referencetrackertypes.h" +#include "referencetrackertypes.hpp" enum class CreateComInterfaceFlagsEx : int32_t { diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 3d9b988297b478..3f21c9cbcf682d 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -21,8 +21,6 @@ namespace InteropLib // Clean up the interop library. void Shutdown() noexcept; -#ifdef _WIN32 - namespace Com { // See CreateComInterfaceFlags in ComWrappers.cs @@ -100,8 +98,6 @@ namespace InteropLib // This should only be called during a runtime's GC phase. HRESULT EndExternalObjectReferenceTracking() noexcept; } - -#endif // _WIN32 } #endif // _INTEROP_INC_INTEROPLIB_H_ diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index 50d1f2dedc7f5e..ed4602ff3aa9c1 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -6,7 +6,9 @@ #include #include -#include "comwrappers.h" +#ifdef FEATURE_COMWRAPPERS +#include "comwrappers.hpp" +#endif // FEATURE_COMWRAPPERS using OBJECTHANDLE = InteropLib::OBJECTHANDLE; using RuntimeCallContext = InteropLibImports::RuntimeCallContext; @@ -15,9 +17,13 @@ namespace InteropLib { void Shutdown() noexcept { +#ifdef FEATURE_COMWRAPPERS TrackerObjectManager::OnShutdown(); +#endif // FEATURE_COMWRAPPERS } +#ifdef FEATURE_COMWRAPPERS + // Exposed COM related API namespace Com { @@ -163,4 +169,6 @@ namespace InteropLib return TrackerObjectManager::EndReferenceTracking(); } } + +#endif // FEATURE_COMWRAPPERS } diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index 3cc4f70e2d130b..896f8af0ac52fc 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -5,9 +5,9 @@ #ifndef _INTEROP_PLATFORM_H_ #define _INTEROP_PLATFORM_H_ -#include -#include -#include +#include +#include +#include #ifndef _ASSERTE #define _ASSERTE(x) assert((x)) @@ -21,6 +21,8 @@ #define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return hr; } } #define RETURN_VOID_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return; } } +#define FEATURE_COMWRAPPERS + #endif // _WIN32 #define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") diff --git a/src/coreclr/src/interop/referencetrackertypes.h b/src/coreclr/src/interop/referencetrackertypes.hpp similarity index 100% rename from src/coreclr/src/interop/referencetrackertypes.h rename to src/coreclr/src/interop/referencetrackertypes.hpp diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index d4bfbe8dfca40d..5bbccf0d845bb2 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include "comwrappers.h" +#include "comwrappers.hpp" #include using OBJECTHANDLE = InteropLib::OBJECTHANDLE; From 1bb76c9e48face489c0b00d5a99505b1643718cc Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 14 Feb 2020 13:03:53 -0800 Subject: [PATCH 49/83] Update Windows' build in response to macOS build fixes. --- src/coreclr/src/interop/comwrappers.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 427df083d6672b..d7fa94bab1337f 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -5,6 +5,8 @@ #include "comwrappers.hpp" #include +#include // placement new + using OBJECTHANDLE = InteropLib::OBJECTHANDLE; using AllocScenario = InteropLibImports::AllocScenario; @@ -81,7 +83,7 @@ namespace ABI #ifdef _DEBUG // Poison unused portions of the section. - std::memset(section, 0xff, sizeof(void*)); + ::memset(section, 0xff, sizeof(void*)); #endif section += sizeof(void*); @@ -375,7 +377,7 @@ HRESULT ManagedObjectWrapper::Create( ABI::ComInterfaceEntry* runtimeDefined = nullptr; if (0 < runtimeDefinedCount) { - std::memcpy(runtimeDefinedOffset, runtimeDefinedLocal, totalRuntimeDefinedSize); + ::memcpy(runtimeDefinedOffset, runtimeDefinedLocal, totalRuntimeDefinedSize); runtimeDefined = reinterpret_cast(runtimeDefinedOffset); } @@ -646,7 +648,7 @@ HRESULT NativeObjectWrapperContext::Create( void* runtimeContext = cxtMem + sizeof(NativeObjectWrapperContext); // Contract specifically requires zeroing out runtime context. - std::memset(runtimeContext, 0, runtimeContextSize); + ::memset(runtimeContext, 0, runtimeContextSize); NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ runtimeContext, trackerObject }; From 96ca3dc4c972e8abc9fa24c4a0bf2522ae54d198 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 14 Feb 2020 13:34:00 -0800 Subject: [PATCH 50/83] Release build fixes Some CRST functions don't exist in Release builds. Change usage of COMPlusThrow() to COMPlusThrowHR(). --- src/coreclr/src/vm/interoplibinterface.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 96064116da7b67..1309e8fad817fc 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -214,7 +214,11 @@ namespace bool IsLockHeld() { WRAPPER_NO_CONTRACT; +#if _DEBUG return (_lock.OwnedByCurrentThread() != FALSE); +#else + return true; +#endif // !_DEBUG } // Create a managed IEnumerable instance for this collection. @@ -632,7 +636,7 @@ namespace sizeof(ExternalObjectContext), &resultHolder); if (FAILED(hr)) - COMPlusThrow(hr); + COMPlusThrowHR(hr); // Call the implementation to create an external object wrapper. gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); From c05b565d61f93aa306a86c243f77afb7361986ba Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 14 Feb 2020 21:29:04 -0800 Subject: [PATCH 51/83] Remove ATL dependency from Mock Reference Tracker runtime library. --- src/coreclr/src/interop/comwrappers.hpp | 5 +- .../ReferenceTrackerRuntime.cpp | 19 +++--- .../src/Interop/COM/NativeServer/Servers.h | 52 --------------- .../tests/src/Interop/common/ComHelpers.h | 66 +++++++++++++++++++ 4 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.hpp b/src/coreclr/src/interop/comwrappers.hpp index 2fe09314c2efd3..1d6ad88516b825 100644 --- a/src/coreclr/src/interop/comwrappers.hpp +++ b/src/coreclr/src/interop/comwrappers.hpp @@ -205,8 +205,7 @@ struct ComHolder ComHolder& operator=(_Inout_ ComHolder&& other) { - Release(); - p = other.Detach(); + Attach(other.Detach()); return (*this); } @@ -240,8 +239,6 @@ struct ComHolder void Attach(_In_opt_ T* i) noexcept { Release(); - if (i != nullptr) - (void)i->AddRef(); p = i; } diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp index 50580d71da4bc8..f0a0f30b277e76 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -6,7 +6,6 @@ #include #include #include -#include namespace API { @@ -79,7 +78,7 @@ namespace std::atomic _trackerSourceCount; bool _connected; std::atomic _elementId; - std::unordered_map> _elements; + std::unordered_map> _elements; TrackerObject(size_t id) : _id{ id }, _trackerSourceCount{ 0 }, _connected{ false }, _elementId{ 1 } { } @@ -91,7 +90,7 @@ namespace auto curr = std::begin(_elements); while (curr != std::end(_elements)) { - CComPtr mowMaybe; + ComSmartPtr mowMaybe; if (S_OK == curr->second->QueryInterface(&mowMaybe)) { if (shouldPeg) @@ -116,7 +115,7 @@ namespace try { *id = _elementId; - if (!_elements.insert(std::make_pair(*id, CComPtr{ c })).second) + if (!_elements.insert(std::make_pair(*id, ComSmartPtr{ c })).second) return S_FALSE; _elementId++; @@ -126,7 +125,7 @@ namespace return E_OUTOFMEMORY; } - CComPtr mowMaybe; + ComSmartPtr mowMaybe; if (S_OK == c->QueryInterface(&mowMaybe)) (void)mowMaybe->AddRefFromReferenceTracker(); @@ -139,7 +138,7 @@ namespace if (iter == std::end(_elements)) return S_FALSE; - CComPtr mowMaybe; + ComSmartPtr mowMaybe; if (S_OK == iter->second->QueryInterface(&mowMaybe)) { (void)mowMaybe->ReleaseFromReferenceTracker(); @@ -172,13 +171,13 @@ namespace class TrackerRuntimeManagerImpl : public API::IReferenceTrackerManager { - CComPtr _runtimeServices; - std::list> _objects; + ComSmartPtr _runtimeServices; + std::list> _objects; public: void RecordObject(_In_ TrackerObject* obj) { - _objects.push_back(CComPtr{ obj }); + _objects.push_back(ComSmartPtr{ obj }); if (_runtimeServices != nullptr) _runtimeServices->AddMemoryPressure(sizeof(TrackerObject)); @@ -278,7 +277,7 @@ namespace { assert(pCallback != nullptr); - CComPtr mowMaybe; + ComSmartPtr mowMaybe; for (auto& e : _elements) { if (S_OK == e.second->QueryInterface(&mowMaybe)) diff --git a/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h b/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h index 2ff3f88ce0b1c7..7f1d0b41b56f67 100644 --- a/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h +++ b/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h @@ -68,58 +68,6 @@ struct CoreShimComActivation } }; -template -struct ComSmartPtr -{ - ComSmartPtr() - : p{} - { } - - ComSmartPtr(_In_ const ComSmartPtr &) = delete; - ComSmartPtr(_Inout_ ComSmartPtr &&) = delete; - - ComSmartPtr& operator=(_In_ const ComSmartPtr &) = delete; - ComSmartPtr& operator=(_Inout_ ComSmartPtr &&) = delete; - - ~ComSmartPtr() - { - if (p != nullptr) - p->Release(); - } - - operator T*() - { - return p; - } - - T** operator&() - { - return &p; - } - - T* operator->() - { - return p; - } - - void Attach(_In_opt_ T *t) - { - if (p != nullptr) - p->Release(); - - p = t; - } - - T *Detach() - { - T *tmp = p; - p = nullptr; - return tmp; - } - - T *p; -}; - #ifndef COM_CLIENT #include diff --git a/src/coreclr/tests/src/Interop/common/ComHelpers.h b/src/coreclr/tests/src/Interop/common/ComHelpers.h index f6a35d30c4a3ea..2c72507136ceda 100644 --- a/src/coreclr/tests/src/Interop/common/ComHelpers.h +++ b/src/coreclr/tests/src/Interop/common/ComHelpers.h @@ -331,3 +331,69 @@ class ClassFactoryLicense : public UnknownImpl, public IClassFactory2 DEFINE_REF_COUNTING(); }; + +template +struct ComSmartPtr +{ + T* p; + + ComSmartPtr() + : p{ nullptr } + { } + + ComSmartPtr(_In_ T* t) + : p{ t } + { + if (p != nullptr) + (void)p->AddRef(); + } + + ComSmartPtr(_In_ const ComSmartPtr&) = delete; + + ComSmartPtr(_Inout_ ComSmartPtr&& other) + : p{ other.Detach() } + { } + + ~ComSmartPtr() + { + if (p != nullptr) + (void)p->Release(); + } + + ComSmartPtr& operator=(_In_ const ComSmartPtr&) = delete; + + ComSmartPtr& operator=(_Inout_ ComSmartPtr&& other) + { + Attach(other.Detach()); + return (*this); + } + + operator T*() + { + return p; + } + + T** operator&() + { + return &p; + } + + T* operator->() + { + return p; + } + + void Attach(_In_opt_ T* t) noexcept + { + if (p != nullptr) + (void)p->Release(); + p = t; + } + + T* Detach() noexcept + { + T* tmp = p; + p = nullptr; + return tmp; + } +}; From 6e4fcce65260c43c26e48cd08b435a75626dcc8f Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 14 Feb 2020 22:11:10 -0800 Subject: [PATCH 52/83] Always include ComHelper.h in COM tests. --- src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h b/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h index 7f1d0b41b56f67..3e0fccc16e422b 100644 --- a/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h +++ b/src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h @@ -68,9 +68,9 @@ struct CoreShimComActivation } }; -#ifndef COM_CLIENT - #include +#include +#ifndef COM_CLIENT #define DEF_FUNC(n) virtual COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE n #include "NumericTesting.h" From 2006fc6f1472e1bef1b3eb223b8de0d33b113f86 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 14 Feb 2020 22:39:58 -0800 Subject: [PATCH 53/83] Add Release() impl on test ComSmartPtr. --- src/coreclr/tests/src/Interop/common/ComHelpers.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tests/src/Interop/common/ComHelpers.h b/src/coreclr/tests/src/Interop/common/ComHelpers.h index 2c72507136ceda..c90ff7a773a2e0 100644 --- a/src/coreclr/tests/src/Interop/common/ComHelpers.h +++ b/src/coreclr/tests/src/Interop/common/ComHelpers.h @@ -356,8 +356,7 @@ struct ComSmartPtr ~ComSmartPtr() { - if (p != nullptr) - (void)p->Release(); + Release(); } ComSmartPtr& operator=(_In_ const ComSmartPtr&) = delete; @@ -385,8 +384,7 @@ struct ComSmartPtr void Attach(_In_opt_ T* t) noexcept { - if (p != nullptr) - (void)p->Release(); + Release(); p = t; } @@ -396,4 +394,13 @@ struct ComSmartPtr p = nullptr; return tmp; } + + void Release() noexcept + { + if (p != nullptr) + { + (void)p->Release(); + p = nullptr; + } + } }; From 0041a357570f20febbafdd884fc93de249bfd364 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 00:29:55 -0800 Subject: [PATCH 54/83] Create a "support" and "nosupport" ComWrappers implementation. --- .../System.Private.CoreLib.csproj | 5 +- .../InteropServices/ComWrappers.Nosupport.cs | 68 +++++++++++++++++++ ...{ComWrappers.cs => ComWrappers.Support.cs} | 2 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs rename src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/{ComWrappers.cs => ComWrappers.Support.cs} (99%) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 2d705c4960e09d..58d92b799e3a0b 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -326,7 +326,7 @@ - + @@ -382,6 +382,9 @@ + + + Common\Interop\Windows\Interop.BOOL.cs diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs new file mode 100644 index 00000000000000..c7ac2e129bbf58 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices +{ + [Flags] + public enum CreateComInterfaceFlags + { + None = 0, + CallerDefinedIUnknown = 1, + TrackerSupport = 2, + } + + [Flags] + public enum CreateObjectFlags + { + None = 0, + TrackerObject = 1, + IgnoreCache = 2, + } + + [CLSCompliant(false)] + public abstract class ComWrappers + { + public struct ComInterfaceEntry + { + public Guid IID; + public IntPtr Vtable; + } + + public struct ComInterfaceDispatch + { + public IntPtr vftbl; + + public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class + { + throw new PlatformNotSupportedException(); + } + } + + public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) + { + throw new PlatformNotSupportedException(); + } + + protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); + + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + { + throw new PlatformNotSupportedException(); + } + + protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + + protected virtual void ReleaseObjects(IEnumerable objects) + { + throw new PlatformNotSupportedException(); + } + + public void RegisterForReferenceTrackerHost() + { + throw new PlatformNotSupportedException(); + } + } +} \ No newline at end of file diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Support.cs similarity index 99% rename from src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs rename to src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Support.cs index 6d8281be651d88..ae96ceab0dcaef 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Support.cs @@ -54,7 +54,7 @@ public enum CreateObjectFlags /// Class for managing wrappers of COM IUnknown types. /// [CLSCompliant(false)] - public abstract class ComWrappers + public abstract partial class ComWrappers { /// /// Interface type and pointer to targeted VTable. From 05c003540bace4aac69c965785d11db97543c702 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 08:08:21 -0800 Subject: [PATCH 55/83] Properly add a platform not supported implementation for ComWrappers. --- .../src/System.Private.CoreLib/System.Private.CoreLib.csproj | 2 +- .../InteropServices/{ComWrappers.Support.cs => ComWrappers.cs} | 0 .../src/System.Private.CoreLib.Shared.projitems | 1 + .../InteropServices/ComWrappers.PlatformNotSupported.cs} | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) rename src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/{ComWrappers.Support.cs => ComWrappers.cs} (100%) rename src/{coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs => libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs} (98%) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 58d92b799e3a0b..f41fc1b3b9934c 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -326,7 +326,7 @@ - + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Support.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs similarity index 100% rename from src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Support.cs rename to src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs 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 62e69dd3b3663d..00bc2b6f78ea89 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 @@ -1491,6 +1491,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs similarity index 98% rename from src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index c7ac2e129bbf58..f56ed5b000f4a2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.Nosupport.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; namespace System.Runtime.InteropServices { From 8a697325c14b6182737b4e1fe3118e28b344ed03 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 10:46:46 -0800 Subject: [PATCH 56/83] Really remove the ComWrappers.NoSupport.cs file. --- .../src/System.Private.CoreLib/System.Private.CoreLib.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index f41fc1b3b9934c..2d705c4960e09d 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -382,9 +382,6 @@ - - - Common\Interop\Windows\Interop.BOOL.cs From e8d616ca05b917e1fe4a53804aafd44dbd9bcc35 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 11:28:27 -0800 Subject: [PATCH 57/83] Forgot GetIUnknownImpl() API in the not supported version of ComWrappers. --- .../InteropServices/ComWrappers.PlatformNotSupported.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index f56ed5b000f4a2..8b5bf5baad88c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -65,5 +65,10 @@ public void RegisterForReferenceTrackerHost() { throw new PlatformNotSupportedException(); } + + protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) + { + throw new PlatformNotSupportedException(); + } } } \ No newline at end of file From b8703b1cc2089356c0a61a5e880d84db96f867de Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 15:39:51 -0800 Subject: [PATCH 58/83] Remove InteropLib shutdown logic. --- src/coreclr/src/interop/comwrappers.hpp | 3 --- src/coreclr/src/interop/inc/interoplib.h | 3 --- src/coreclr/src/interop/interoplib.cpp | 8 -------- .../src/interop/trackerobjectmanager.cpp | 10 ---------- src/coreclr/src/vm/interoplibinterface.cpp | 18 ------------------ src/coreclr/src/vm/interoplibinterface.h | 3 --- 6 files changed, 45 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.hpp b/src/coreclr/src/interop/comwrappers.hpp index 1d6ad88516b825..ed048023e1211a 100644 --- a/src/coreclr/src/interop/comwrappers.hpp +++ b/src/coreclr/src/interop/comwrappers.hpp @@ -180,9 +180,6 @@ class TrackerObjectManager // End the reference tracking process for external object. static HRESULT EndReferenceTracking(); - - // Clean up internal resources used for reference tracking. - static void OnShutdown(); }; // Class used to hold COM objects (i.e. IUnknown base class) diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index 3f21c9cbcf682d..b20b207310f0b2 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -18,9 +18,6 @@ namespace InteropLib { using OBJECTHANDLE = void*; - // Clean up the interop library. - void Shutdown() noexcept; - namespace Com { // See CreateComInterfaceFlags in ComWrappers.cs diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index ed4602ff3aa9c1..3d1b7079ccd22e 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -15,15 +15,7 @@ using RuntimeCallContext = InteropLibImports::RuntimeCallContext; namespace InteropLib { - void Shutdown() noexcept - { -#ifdef FEATURE_COMWRAPPERS - TrackerObjectManager::OnShutdown(); -#endif // FEATURE_COMWRAPPERS - } - #ifdef FEATURE_COMWRAPPERS - // Exposed COM related API namespace Com { diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 5bbccf0d845bb2..7581fef58254c9 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -370,13 +370,3 @@ HRESULT TrackerObjectManager::EndReferenceTracking() return hr; } - -void TrackerObjectManager::OnShutdown() -{ - IReferenceTrackerManager* trackerManager = s_TrackerManager; - if (::InterlockedCompareExchangePointer((void**)&s_TrackerManager, nullptr, trackerManager) == trackerManager) - { - // Make sure s_TrackerManager is either null or a valid pointer. - trackerManager->Release(); - } -} diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 1309e8fad817fc..4771e919a391e4 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -1247,21 +1247,3 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration) #endif // FEATURE_COMINTEROP } - -void Interop::OnRuntimeShutdown() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } CONTRACTL_END; - -#ifdef FEATURE_COMINTEROP - // Release IJupiterGCMgr* - RCWWalker::OnEEShutdown(); - - InteropLib::Shutdown(); - -#endif // FEATURE_COMINTEROP -} diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 89c519ede19bd9..376f2664430e28 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -44,7 +44,4 @@ class Interop // Notify when GC finished static void OnGCFinished(_In_ int nCondemnedGeneration); - - // Notify when the runtime is shutting down. - static void OnRuntimeShutdown(); }; From 07cbc68e61f321d0ffc4e9aba126bf6b556b0721 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 18:08:50 -0800 Subject: [PATCH 59/83] Acquire RefCache at the same time as creating ObjectCache. This avoids the need to handle a possible OOM at an inconvenient time. Only define IsLockHeld() function in debug build. Make usage of NULL consistent in interface cpp file. --- src/coreclr/src/vm/interoplibinterface.cpp | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 4771e919a391e4..fac69bfe25b954 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -107,9 +107,9 @@ namespace { } ~ExternalWrapperResultHolder() { - if (Result.Context != nullptr) + if (Result.Context != NULL) InteropLib::Com::DestroyWrapperForExternal(Result.Context); - if (Result.AgileRef != nullptr) + if (Result.AgileRef != NULL) (void)Result.AgileRef->Release(); } InteropLib::Com::ExternalWrapperResult* operator&() @@ -123,11 +123,13 @@ namespace ExternalObjectContext* DetachContext() { ExternalObjectContext* t = GetContext(); - Result.Context = nullptr; + Result.Context = NULL; return t; } }; + using ExtObjCxtRefCache = RCWRefCache; + class ExtObjCxtCache { static Volatile g_Instance; @@ -139,7 +141,7 @@ namespace { NOTHROW; GC_NOTRIGGER; - POSTCONDITION(RETVAL != NULL); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); } CONTRACT_END; @@ -204,21 +206,28 @@ namespace friend struct InteropLibImports::RuntimeCallContext; SHash _hashMap; Crst _lock; + ExtObjCxtRefCache* _refCache; ExtObjCxtCache() : _lock(CrstExternalObjectContextCache, CRST_UNSAFE_COOPGC) + , _refCache(GetAppDomain()->GetRCWRefCache()) { } ~ExtObjCxtCache() = default; public: +#if _DEBUG bool IsLockHeld() { WRAPPER_NO_CONTRACT; -#if _DEBUG return (_lock.OwnedByCurrentThread() != FALSE); -#else - return true; -#endif // !_DEBUG + } +#endif // _DEBUG + + // Get the associated reference cache with this external object cache. + ExtObjCxtRefCache* GetRefCache() + { + WRAPPER_NO_CONTRACT; + return _refCache; } // Create a managed IEnumerable instance for this collection. @@ -391,8 +400,6 @@ namespace // Global instance Volatile ExtObjCxtCache::g_Instance; - using ExtObjCxtRefCache = RCWRefCache; - // Defined handle types for the specific object uses. const HandleType InstanceHandleType{ HNDTYPE_STRONG }; @@ -947,10 +954,10 @@ namespace InteropLibImports // Pointer to cache used to create object references. ExtObjCxtRefCache* RefCache; - RuntimeCallContext(_In_ ExtObjCxtCache* cache, _In_ ExtObjCxtRefCache* refCache) + RuntimeCallContext(_In_ ExtObjCxtCache* cache) : Curr{ cache->_hashMap.Begin() } , End{ cache->_hashMap.End() } - , RefCache{ refCache } + , RefCache{ cache->GetRefCache() } { } }; @@ -1192,25 +1199,22 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) // See Interop::OnGCFinished() if (nCondemnedGeneration >= 2) { - EX_TRY + // If no cache exists, then there is nothing to do here. + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); + if (cache != NULL) { - ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - RCWRefCache *refCache = GetAppDomain()->GetRCWRefCache(); + ExtObjCxtRefCache* refCache = cache->GetRefCache(); // Reset the ref cache refCache->ResetDependentHandles(); // Create a call context for the InteropLib. - InteropLibImports::RuntimeCallContext cxt(cache, refCache); + InteropLibImports::RuntimeCallContext cxt(cache); (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt); // Shrink cache and clear unused handles. refCache->ShrinkDependentHandles(); } - EX_CATCH - { - } - EX_END_CATCH(RethrowCorruptingExceptions) } #endif // FEATURE_COMINTEROP From c609b1cf18d2b1dba3c7cfcbfc903d4e0675924a Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 21:51:36 -0800 Subject: [PATCH 60/83] Add StressLogs to InteropLibInterface --- src/coreclr/src/vm/interoplibinterface.cpp | 28 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index fac69bfe25b954..03b75727653dca 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -284,10 +284,12 @@ namespace if (inst->ThreadContext == threadContext && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet(withFlags))) { - // Separate the wrapper from the tracker runtime - // prior to passing this onto the caller. + // Separate the wrapper from the tracker runtime prior to + // passing this onto the caller. This call is okay to make + // even if the instance isn't from the tracker runtime. InteropLib::Com::SeparateWrapperFromTrackerRuntime(inst); gc.arrRef->SetAt(objCount, inst->GetObjectRef()); + STRESS_LOG1(LF_INTEROP, LL_INFO100, "Add EOC to Enumerable: 0x%p\n", inst); } } } @@ -567,6 +569,7 @@ namespace // A new managed object wrapper was created, remove the object from the holder. // No AddRef() here since the wrapper should be created with a reference. wrapperRaw = newWrapper.Extract(); + STRESS_LOG1(LF_INTEROP, LL_INFO100, "Created MOW: 0x%p\n", wrapperRaw); } else { @@ -578,6 +581,7 @@ namespace hr = InteropLib::Com::IsActiveWrapper(wrapper); if (hr == S_FALSE) { + STRESS_LOG1(LF_INTEROP, LL_INFO100, "Reactivating MOW: 0x%p\n", wrapperRaw); OBJECTHANDLE h = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); hr = InteropLib::Com::ReactivateWrapper(wrapper, static_cast(h)); } @@ -687,6 +691,7 @@ namespace // Detach from the holder to avoid cleanup. (void)resultHolder.DetachContext(); + STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created EOC (Ignore Cache: %d): 0x%p\n", (int)ignoreCache, extObjCxt); } _ASSERTE(extObjCxt->IsActive()); @@ -938,6 +943,7 @@ namespace InteropLibImports gc.objRef, trackerTargetFlags); + STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created Target for External: 0x%p => 0x%p\n", gc.objRef, *trackerTarget); GCPROTECT_END(); } END_EXTERNAL_ENTRYPOINT; @@ -1020,6 +1026,7 @@ namespace InteropLibImports if (source->PassiveGetSyncBlock() == target->PassiveGetSyncBlock()) return S_FALSE; + STRESS_LOG2(LF_INTEROP, LL_INFO1000, "Found reference path: 0x%p => 0x%p\n", source, target); return runtimeContext->RefCache->AddReferenceFromObjectToObject(source, target); } } @@ -1117,6 +1124,7 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) } CONTRACTL_END; + STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying MOW: 0x%p\n", wrapper); InteropLib::Com::DestroyWrapperForObject(wrapper); } @@ -1135,6 +1143,7 @@ void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) _ASSERTE(!context->IsActive()); #endif + STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying EOC: 0x%p\n", contextRaw); InteropLib::Com::DestroyWrapperForExternal(contextRaw); } @@ -1154,8 +1163,11 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context _ASSERTE(context->IsActive()); context->MarkCollected(); + bool inCache = context->IsSet(ExternalObjectContext::Flags_InCache); + STRESS_LOG2(LF_INTEROP, LL_INFO100, "Mark Collected EOC (In Cache: %d): 0x%p\n", (int)inCache, contextRaw); + // Verify the caller didn't ignore the cache during creation. - if (context->IsSet(ExternalObjectContext::Flags_InCache)) + if (inCache) { ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); cache->Remove(context); @@ -1203,6 +1215,7 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); if (cache != NULL) { + STRESS_LOG0(LF_INTEROP, LL_INFO10000, "Begin Reference Tracking\n"); ExtObjCxtRefCache* refCache = cache->GetRefCache(); // Reset the ref cache @@ -1247,7 +1260,14 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration) // // See Interop::OnGCStarted() if (nCondemnedGeneration >= 2) - (void)InteropLib::Com::EndExternalObjectReferenceTracking(); + { + ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow(); + if (cache != NULL) + { + (void)InteropLib::Com::EndExternalObjectReferenceTracking(); + STRESS_LOG0(LF_INTEROP, LL_INFO10000, "End Reference Tracking\n"); + } + } #endif // FEATURE_COMINTEROP } From 20a53855740780497f83fff4c3c03ff956d701b2 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 15 Feb 2020 22:25:14 -0800 Subject: [PATCH 61/83] Cast OBJECTREFs so they work in StressLog macros. --- src/coreclr/src/vm/interoplibinterface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 03b75727653dca..88a38b17c75d16 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -943,7 +943,7 @@ namespace InteropLibImports gc.objRef, trackerTargetFlags); - STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created Target for External: 0x%p => 0x%p\n", gc.objRef, *trackerTarget); + STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created Target for External: 0x%p => 0x%p\n", OBJECTREFToObject(gc.objRef), *trackerTarget); GCPROTECT_END(); } END_EXTERNAL_ENTRYPOINT; @@ -1026,7 +1026,9 @@ namespace InteropLibImports if (source->PassiveGetSyncBlock() == target->PassiveGetSyncBlock()) return S_FALSE; - STRESS_LOG2(LF_INTEROP, LL_INFO1000, "Found reference path: 0x%p => 0x%p\n", source, target); + STRESS_LOG2(LF_INTEROP, LL_INFO1000, "Found reference path: 0x%p => 0x%p\n", + OBJECTREFToObject(source), + OBJECTREFToObject(target)); return runtimeContext->RefCache->AddReferenceFromObjectToObject(source, target); } } From 8e97865a1a7eed4a58c6128e9a74aeb1b733dcad Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 19 Feb 2020 15:41:33 -0800 Subject: [PATCH 62/83] Add a ComWrappers feature flag. --- src/coreclr/clr.featuredefines.props | 2 ++ src/coreclr/clrdefinitions.cmake | 1 + .../System.Private.CoreLib.csproj | 4 +++- src/coreclr/src/interop/platform.h | 2 -- src/coreclr/src/vm/ecalllist.h | 6 ++++-- src/coreclr/src/vm/interoplibinterface.cpp | 14 ++++++++------ src/coreclr/src/vm/interoplibinterface.h | 4 ++-- src/coreclr/src/vm/interoputil.cpp | 4 ++++ .../src/System.Private.CoreLib.Shared.projitems | 3 +++ 9 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 7ae2f2f7c8b94c..b4074674e5744e 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -28,6 +28,7 @@ true true true + true true true true @@ -54,6 +55,7 @@ $(DefineConstants);FEATURE_STUBS_AS_IL $(DefineConstants);FEATURE_CLASSIC_COMINTEROP $(DefineConstants);FEATURE_COLLECTIBLE_ALC + $(DefineConstants);FEATURE_COMWRAPPERS $(DefineConstants);FEATURE_COMINTEROP $(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT $(DefineConstants);FEATURE_COMINTEROP_UNMANAGED_ACTIVATION diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 474788010d779a..a58e3af0d76043 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -98,6 +98,7 @@ add_compile_definitions($<$>>:F add_definitions(-DFEATURE_COLLECTIBLE_TYPES) if(CLR_CMAKE_TARGET_WIN32) + add_definitions(-DFEATURE_COMWRAPPERS) add_definitions(-DFEATURE_CLASSIC_COMINTEROP) add_definitions(-DFEATURE_COMINTEROP) add_definitions(-DFEATURE_COMINTEROP_APARTMENT_SUPPORT) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 2d705c4960e09d..d0c8c1b17d145a 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -303,6 +303,9 @@ + + + Common\System\Runtime\InteropServices\IDispatch.cs @@ -326,7 +329,6 @@ - diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h index 896f8af0ac52fc..b21ec99cd8ca85 100644 --- a/src/coreclr/src/interop/platform.h +++ b/src/coreclr/src/interop/platform.h @@ -21,8 +21,6 @@ #define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return hr; } } #define RETURN_VOID_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return; } } -#define FEATURE_COMWRAPPERS - #endif // _WIN32 #define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.") diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 84e62ad98ee73e..1ae20b1ff13685 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -991,13 +991,15 @@ FCFuncStart(gWinRTTypeNameConverterFuncs) FCFuncElement("GetTypeFromWinRTTypeName", StubHelpers::WinRTTypeNameConverter__GetTypeFromWinRTTypeName) FCFuncEnd() +#endif // FEATURE_COMINTEROP + +#ifdef FEATURE_COMWRAPPERS FCFuncStart(gComWrappersFuncs) QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl) QCFuncElement("GetOrCreateComInterfaceForObjectInternal", ComWrappersNative::GetOrCreateComInterfaceForObject) QCFuncElement("GetOrCreateObjectForComInstanceInternal", ComWrappersNative::GetOrCreateObjectForComInstance) FCFuncEnd() - -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMWRAPPERS FCFuncStart(gMngdRefCustomMarshalerFuncs) FCFuncElement("CreateMarshaler", MngdRefCustomMarshaler::CreateMarshaler) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 88a38b17c75d16..7a451648bb6e96 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -1033,7 +1033,7 @@ namespace InteropLibImports } } -#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject( _In_ QCall::ObjectHandleOnStack comWrappersImpl, @@ -1176,7 +1176,7 @@ void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* context } } -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMWRAPPERS void Interop::OnGCStarted(_In_ int nCondemnedGeneration) { @@ -1199,7 +1199,9 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) // 2. Dependent handles // RCWWalker::OnGCStarted(nCondemnedGeneration); +#endif // FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS // // Note that we could get nested GCStart/GCEnd calls, such as : // GCStart for Gen 2 background GC @@ -1231,8 +1233,7 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration) refCache->ShrinkDependentHandles(); } } - -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMWRAPPERS } void Interop::OnGCFinished(_In_ int nCondemnedGeneration) @@ -1249,7 +1250,9 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration) // Tell Jupiter GC has finished // RCWWalker::OnGCFinished(nCondemnedGeneration); +#endif // FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS // // Note that we could get nested GCStart/GCEnd calls, such as : // GCStart for Gen 2 background GC @@ -1270,6 +1273,5 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration) STRESS_LOG0(LF_INTEROP, LL_INFO10000, "End Reference Tracking\n"); } } - -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMWRAPPERS } diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 376f2664430e28..219c544e23366b 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -6,7 +6,7 @@ // Interface between the VM and Interop library. // -#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS // Native calls for the managed ComWrappers API class ComWrappersNative @@ -34,7 +34,7 @@ class ComWrappersNative static void MarkExternalComObjectContextCollected(_In_ void* context); }; -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMWRAPPERS class Interop { diff --git a/src/coreclr/src/vm/interoputil.cpp b/src/coreclr/src/vm/interoputil.cpp index aef8966d39ffb7..fc87cf9277d538 100644 --- a/src/coreclr/src/vm/interoputil.cpp +++ b/src/coreclr/src/vm/interoputil.cpp @@ -1938,9 +1938,11 @@ void MinorCleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) if (pRCW) pRCW->MinorCleanup(); +#ifdef FEATURE_COMWRAPPERS void* eoc; if (pInteropInfo->TryGetExternalComObjectContext(&eoc)) ComWrappersNative::MarkExternalComObjectContextCollected(eoc); +#endif // FEATURE_COMWRAPPERS } void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) @@ -1982,6 +1984,7 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) pCCW->Cleanup(); } +#ifdef FEATURE_COMWRAPPERS void* mocw; if (pInteropInfo->TryGetManagedObjectComWrapper(&mocw)) { @@ -1995,6 +1998,7 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) (void)pInteropInfo->TrySetExternalComObjectContext(NULL, eoc); ComWrappersNative::DestroyExternalComObjectContext(eoc); } +#endif // FEATURE_COMWRAPPERS } void ReleaseRCWsInCachesNoThrow(LPVOID pCtxCookie) 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 00bc2b6f78ea89..0db9713ec88e84 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 @@ -1488,6 +1488,9 @@ + + + From 5fa679c45c1baf004b5962b09849be68970c58f3 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 19 Feb 2020 16:14:00 -0800 Subject: [PATCH 63/83] Remove duplicate entry. --- .../src/System.Private.CoreLib.Shared.projitems | 1 - 1 file changed, 1 deletion(-) 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 0db9713ec88e84..3ee3d97192bf0d 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 @@ -1494,7 +1494,6 @@ - From e8365071be5aeadaafe92e14bb7af6e62ff22618 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 21 Feb 2020 12:31:27 -0800 Subject: [PATCH 64/83] Review feedback. --- .../Runtime/InteropServices/ComWrappers.cs | 25 +++++++++++++------ .../src/dlls/mscoree/coreclr/CMakeLists.txt | 2 +- src/coreclr/src/interop/inc/interoplib.h | 2 +- src/coreclr/src/vm/interoplibinterface.cpp | 14 +++++------ .../src/Interop/COM/ComWrappers/Program.cs | 14 +++++------ .../src/Resources/Strings.resx | 4 +-- .../ComWrappers.PlatformNotSupported.cs | 4 +-- .../ref/System.Runtime.InteropServices.cs | 4 +-- 8 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index ae96ceab0dcaef..489da6ab692e9b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -21,6 +21,12 @@ public enum CreateComInterfaceFlags /// /// The caller will provide an IUnknown Vtable. /// + /// + /// This is useful in scenarios when the caller has no need to rely on an IUnknown instance + /// that is used when running managed code is not possible (i.e. during a GC). In traditional + /// COM scenarios this is common, but scenarios involving Reference Tracker hosting + /// calling of the IUnknown API during a GC is possible. + /// CallerDefinedIUnknown = 1, /// @@ -45,9 +51,9 @@ public enum CreateObjectFlags TrackerObject = 1, /// - /// Ignore the internal cache when creating an object. + /// Ignore any internal caching and always create a unique instance. /// - IgnoreCache = 2, + UniqueInstance = 2, } /// @@ -67,8 +73,7 @@ public struct ComInterfaceEntry public Guid IID; /// - /// Must be pinned memory that is owned by the implementer of . - /// The memory must live as long as any COM interface consuming the table exists. + /// Memory must have the same lifetime as the memory returned from the call to . /// public IntPtr Vtable; } @@ -171,6 +176,8 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// Returns a managed object associated with the supplied external COM object. /// /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. + /// + /// If the object cannot be created and null is returned, the call to will throw a . /// protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); @@ -195,17 +202,21 @@ internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerabl => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects); /// - /// Register this class's implementation to be used when a Reference Tracker Host instance is requested from another runtime. + /// Register this class's implementation to be used as the single global instance. /// /// /// This function can only be called a single time. Subsequent calls to this function will result /// in a being thrown. + /// + /// Scenarios where the global instance may be used are: + /// * Object tracking via the and flags. + /// * Usage of COM related Marshal APIs. /// - public void RegisterForReferenceTrackerHost() + public void RegisterAsGlobalInstance() { if (null != Interlocked.CompareExchange(ref s_globalInstance, this, null)) { - throw new InvalidOperationException(SR.InvalidOperation_ResetReferenceTrackerHostCallbacks); + throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance); } } diff --git a/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt index 8db14ec94b7a24..e86e91f6a69e88 100644 --- a/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt @@ -104,8 +104,8 @@ set(CORECLR_LIBRARIES gcinfo # Condition="'$(TargetCpu)'=='amd64' or '$(TargetCpu)' == 'arm' or '$(TargetCpu)' == 'arm64'" ildbsymlib utilcode - libraries-native v3binder + libraries-native interop ) diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index b20b207310f0b2..e68f1857cd3398 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -63,7 +63,7 @@ namespace InteropLib { CreateObjectFlags_None = 0, CreateObjectFlags_TrackerObject = 1, - CreateObjectFlags_IgnoreCache = 2, + CreateObjectFlags_UniqueInstance = 2, }; // Allocate a wrapper context for an external object. diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 7a451648bb6e96..f417f1fd489b77 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -624,9 +624,9 @@ namespace ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); - // Check if the cache should be queried. - bool ignoreCache = !!(flags & CreateObjectFlags::CreateObjectFlags_IgnoreCache); - if (!ignoreCache) + // Check if the user requested a unique instance. + bool uniqueInstance = !!(flags & CreateObjectFlags::CreateObjectFlags_UniqueInstance); + if (!uniqueInstance) { // Query the external object cache ExtObjCxtCache::LockHolder lock(cache); @@ -658,7 +658,7 @@ namespace DWORD flags = (resultHolder.Result.FromTrackerRuntime ? ExternalObjectContext::Flags_ReferenceTracker : ExternalObjectContext::Flags_None) | - (ignoreCache + (uniqueInstance ? ExternalObjectContext::Flags_None : ExternalObjectContext::Flags_InCache); ExternalObjectContext::Construct( @@ -668,7 +668,7 @@ namespace gc.objRef->GetSyncBlockIndex(), flags); - if (ignoreCache) + if (uniqueInstance) { extObjCxt = resultHolder.GetContext(); } @@ -680,7 +680,7 @@ namespace } // If the returned context matches the new context it means the - // new context was inserted or the cache is being ignored. + // new context was inserted or a unique instance was requested. if (extObjCxt == resultHolder.GetContext()) { // Update the object's SyncBlock with a handle to the context for runtime cleanup. @@ -691,7 +691,7 @@ namespace // Detach from the holder to avoid cleanup. (void)resultHolder.DetachContext(); - STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created EOC (Ignore Cache: %d): 0x%p\n", (int)ignoreCache, extObjCxt); + STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created EOC (Unique Instance: %d): 0x%p\n", (int)uniqueInstance, extObjCxt); } _ASSERTE(extObjCxt->IsActive()); diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index cfe32d41f2402b..c6a37837a19d47 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -330,7 +330,7 @@ static void ValidateCreateObjectCachingScenario() // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.IgnoreCache); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); Assert.AreNotEqual(trackerObj1, trackerObj3); } @@ -340,23 +340,23 @@ static void ValidateIUnknownImpls() static void ValidateGlobalInstanceScenarios() { Console.WriteLine($"Running {nameof(ValidateGlobalInstanceScenarios)}..."); - Console.WriteLine($"Validate RegisterForReferenceTrackerHost()..."); + Console.WriteLine($"Validate RegisterAsGlobalInstance()..."); var wrappers1 = TestComWrappers.Global; - wrappers1.RegisterForReferenceTrackerHost(); + wrappers1.RegisterAsGlobalInstance(); Assert.Throws( () => { - wrappers1.RegisterForReferenceTrackerHost(); - }, "Should not be able to re-register for ReferenceTrackerHost"); + wrappers1.RegisterAsGlobalInstance(); + }, "Should not be able to re-register for global ComWrappers"); var wrappers2 = new TestComWrappers(); Assert.Throws( () => { - wrappers2.RegisterForReferenceTrackerHost(); - }, "Should not be able to reset for ReferenceTrackerHost"); + wrappers2.RegisterAsGlobalInstance(); + }, "Should not be able to reset for global ComWrappers"); Console.WriteLine($"Validate NotifyEndOfReferenceTrackingOnThread()..."); diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 518326b9b26bc5..0639dc7d407946 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3652,8 +3652,8 @@ Type '{0}' has more than one COM unregistration function. - - Attempt to update previously set default ReferenceTrackerHost callbacks. + + Attempt to update previously set global instance. The callback populated its buffer with ill-formed UTF-8 data. Callbacks are required to populate the buffer only with well-formed UTF-8 data. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index 8b5bf5baad88c2..868221095457b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -20,7 +20,7 @@ public enum CreateObjectFlags { None = 0, TrackerObject = 1, - IgnoreCache = 2, + UniqueInstance = 2, } [CLSCompliant(false)] @@ -61,7 +61,7 @@ protected virtual void ReleaseObjects(IEnumerable objects) throw new PlatformNotSupportedException(); } - public void RegisterForReferenceTrackerHost() + public void RegisterAsGlobalInstance() { throw new PlatformNotSupportedException(); } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 226c7ab8ea929a..d6ab32ca603ae9 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -995,7 +995,7 @@ public enum CreateObjectFlags { None = 0, TrackerObject = 1, - IgnoreCache = 2, + UniqueInstance = 2, } [System.CLSCompliantAttribute(false)] public abstract class ComWrappers @@ -1015,7 +1015,7 @@ public struct ComInterfaceDispatch public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } - public void RegisterForReferenceTrackerHost() { } + public void RegisterAsGlobalInstance() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } } } From e60bce2e48c27f36a478d487ad131739cdbe70af Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 21 Feb 2020 17:22:41 -0800 Subject: [PATCH 65/83] Add Type? to the GetOrCreateObjectForComInstance() API call. --- .../Runtime/InteropServices/ComWrappers.cs | 19 ++++++++------- src/coreclr/src/vm/interoplibinterface.cpp | 23 ++++++++++++++----- src/coreclr/src/vm/interoplibinterface.h | 1 + src/coreclr/src/vm/metasig.h | 2 +- src/coreclr/src/vm/mscorlib.h | 2 +- .../src/Interop/COM/ComWrappers/Program.cs | 16 ++++++------- .../ComWrappers.PlatformNotSupported.cs | 4 ++-- .../ref/System.Runtime.InteropServices.cs | 4 ++-- 8 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 489da6ab692e9b..713723cde7f092 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -38,7 +38,7 @@ public enum CreateComInterfaceFlags } /// - /// Enumeration of flags for . + /// Enumeration of flags for . /// [Flags] public enum CreateObjectFlags @@ -151,21 +151,23 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// /// Object to import for usage into the .NET runtime. /// Flags used to describe the external object. + /// An optional that represents the type of the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) { if (externalComObject == IntPtr.Zero) throw new ArgumentNullException(nameof(externalComObject)); ComWrappers impl = this; + Type? targetTypeLocal = targetType; object? retValue = null; - GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref retValue)); + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref targetTypeLocal), ObjectHandleOnStack.Create(ref retValue)); return retValue!; } [DllImport(RuntimeHelpers.QCall)] - private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack retValue); + private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack targetType, ObjectHandleOnStack retValue); /// /// Create a managed object for the object pointed at by respecting the values of . @@ -173,17 +175,18 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// Object to import for usage into the .NET runtime. /// IAgileReference pointing at the object to import into the .NET runtime. /// Flags used to describe the external object. + /// An optional that represents the type of the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. /// /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. /// - /// If the object cannot be created and null is returned, the call to will throw a . + /// If the object cannot be created and null is returned, the call to will throw a . /// - protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); // Call to execute the abstract instance function - internal static object CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) - => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags); + internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType) + => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags, targetType); /// /// Called when a request is made for a collection of objects to be released. diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index f417f1fd489b77..06a5f5e5f62d0a 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -438,7 +438,8 @@ namespace _In_ OBJECTREF* implPROTECTED, _In_ IUnknown* externalComObject, _In_ IUnknown* agileObjectRef, - _In_ INT32 flags) + _In_ INT32 flags, + _In_ OBJECTREF* targetTypePROTECTED) { CONTRACTL { @@ -446,17 +447,19 @@ namespace MODE_COOPERATIVE; PRECONDITION(implPROTECTED != NULL); PRECONDITION(externalComObject != NULL); + PRECONDITION(targetTypePROTECTED != NULL); } CONTRACTL_END; OBJECTREF retObjRef; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); args[ARGNUM_2] = PTR_TO_ARGHOLDER(agileObjectRef); args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_4] = OBJECTREF_TO_ARGHOLDER(*targetTypePROTECTED); CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); return retObjRef; @@ -598,7 +601,8 @@ namespace OBJECTREF GetOrCreateObjectForComInstanceInternal( _In_opt_ OBJECTREF impl, _In_ IUnknown* identity, - _In_ CreateObjectFlags flags) + _In_ CreateObjectFlags flags, + _In_opt_ OBJECTREF targetType) { CONTRACT(OBJECTREF) { @@ -615,12 +619,14 @@ namespace struct { OBJECTREF implRef; + OBJECTREF targetTypeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = impl; + gc.targetTypeRef = targetType; ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); @@ -650,7 +656,7 @@ namespace COMPlusThrowHR(hr); // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); + gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags, &gc.targetTypeRef); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); @@ -924,18 +930,21 @@ namespace InteropLibImports struct { OBJECTREF implRef; + OBJECTREF targetTypeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = NULL; // Use the globally registered implementation. + gc.targetTypeRef = NULL; // No target type here. // Get wrapper for external object gc.objRef = GetOrCreateObjectForComInstanceInternal( gc.implRef, externalComObject, - externalObjectFlags); + externalObjectFlags, + gc.targetTypeRef); // Get wrapper for managed object *trackerTarget = GetOrCreateComInterfaceForObjectInternal( @@ -1066,6 +1075,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* ext, _In_ INT32 flags, + _In_ QCall::ObjectHandleOnStack targetTypeMaybe, _Inout_ QCall::ObjectHandleOnStack retValue) { QCALL_CONTRACT; @@ -1089,7 +1099,8 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal( ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), identity, - (CreateObjectFlags)flags); + (CreateObjectFlags)flags, + ObjectToOBJECTREF(*targetTypeMaybe.m_ppObject)); // Set the return value retValue.Set(newObj); diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 219c544e23366b..d826e3a7e96081 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -26,6 +26,7 @@ class ComWrappersNative _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* externalComObject, _In_ INT32 flags, + _In_ QCall::ObjectHandleOnStack targetTypeMaybe, _Inout_ QCall::ObjectHandleOnStack retValue); public: // Lifetime management for COM Wrappers diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index d754a95b6a996b..305cb69024409c 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -197,7 +197,7 @@ DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v)) DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) -DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS), j)) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_Type_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS) C(TYPE), j)) DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 510d9e05060afe..cafbd34424027f 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -463,7 +463,7 @@ DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) -DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_Type_RetObj) DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid) #endif //FEATURE_COMINTEROP diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index c6a37837a19d47..b55817f422cb99 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -195,7 +195,7 @@ public bool DefaultReleaseObjects return entryRaw; } - protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flag, Type? targetType) { Assert.AreNotEqual(agileObj, IntPtr.Zero); @@ -271,7 +271,7 @@ static void ValidateRuntimeTrackerScenario() IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a managed wrapper for the native object. - var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); @@ -323,14 +323,14 @@ static void ValidateCreateObjectCachingScenario() // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); - var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance, null); Assert.AreNotEqual(trackerObj1, trackerObj3); } @@ -407,7 +407,7 @@ public enum FailureMode } - protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags, Type? targetType) { switch (CreateObjectMode) { @@ -451,13 +451,13 @@ static void ValidateBadComWrapperImpl() () => { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); }); try { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); } catch (Exception e) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index 868221095457b9..d42b3a0a093bb3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -49,12 +49,12 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) { throw new PlatformNotSupportedException(); } - protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); protected virtual void ReleaseObjects(IEnumerable objects) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index d6ab32ca603ae9..6914e6ad894d92 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1012,8 +1012,8 @@ public struct ComInterfaceDispatch } public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } - protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); + public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) { throw null; } + protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterAsGlobalInstance() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } From 75e84019cca5aca27895836e509155e83fd9463e Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 24 Feb 2020 11:05:53 -0800 Subject: [PATCH 66/83] Revert "Add Type? to the GetOrCreateObjectForComInstance() API call." This reverts commit d0f6a5b04adbaa8e53445e4474780a9182ff8bde. --- .../Runtime/InteropServices/ComWrappers.cs | 19 +++++++-------- src/coreclr/src/vm/interoplibinterface.cpp | 23 +++++-------------- src/coreclr/src/vm/interoplibinterface.h | 1 - src/coreclr/src/vm/metasig.h | 2 +- src/coreclr/src/vm/mscorlib.h | 2 +- .../src/Interop/COM/ComWrappers/Program.cs | 16 ++++++------- .../ComWrappers.PlatformNotSupported.cs | 4 ++-- .../ref/System.Runtime.InteropServices.cs | 4 ++-- 8 files changed, 28 insertions(+), 43 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 713723cde7f092..489da6ab692e9b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -38,7 +38,7 @@ public enum CreateComInterfaceFlags } /// - /// Enumeration of flags for . + /// Enumeration of flags for . /// [Flags] public enum CreateObjectFlags @@ -151,23 +151,21 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// /// Object to import for usage into the .NET runtime. /// Flags used to describe the external object. - /// An optional that represents the type of the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { if (externalComObject == IntPtr.Zero) throw new ArgumentNullException(nameof(externalComObject)); ComWrappers impl = this; - Type? targetTypeLocal = targetType; object? retValue = null; - GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref targetTypeLocal), ObjectHandleOnStack.Create(ref retValue)); + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref retValue)); return retValue!; } [DllImport(RuntimeHelpers.QCall)] - private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack targetType, ObjectHandleOnStack retValue); + private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack retValue); /// /// Create a managed object for the object pointed at by respecting the values of . @@ -175,18 +173,17 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// Object to import for usage into the .NET runtime. /// IAgileReference pointing at the object to import into the .NET runtime. /// Flags used to describe the external object. - /// An optional that represents the type of the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. /// /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. /// - /// If the object cannot be created and null is returned, the call to will throw a . + /// If the object cannot be created and null is returned, the call to will throw a . /// - protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); + protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType) - => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags, targetType); + internal static object CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) + => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags); /// /// Called when a request is made for a collection of objects to be released. diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 06a5f5e5f62d0a..f417f1fd489b77 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -438,8 +438,7 @@ namespace _In_ OBJECTREF* implPROTECTED, _In_ IUnknown* externalComObject, _In_ IUnknown* agileObjectRef, - _In_ INT32 flags, - _In_ OBJECTREF* targetTypePROTECTED) + _In_ INT32 flags) { CONTRACTL { @@ -447,19 +446,17 @@ namespace MODE_COOPERATIVE; PRECONDITION(implPROTECTED != NULL); PRECONDITION(externalComObject != NULL); - PRECONDITION(targetTypePROTECTED != NULL); } CONTRACTL_END; OBJECTREF retObjRef; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 5); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); args[ARGNUM_2] = PTR_TO_ARGHOLDER(agileObjectRef); args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags); - args[ARGNUM_4] = OBJECTREF_TO_ARGHOLDER(*targetTypePROTECTED); CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); return retObjRef; @@ -601,8 +598,7 @@ namespace OBJECTREF GetOrCreateObjectForComInstanceInternal( _In_opt_ OBJECTREF impl, _In_ IUnknown* identity, - _In_ CreateObjectFlags flags, - _In_opt_ OBJECTREF targetType) + _In_ CreateObjectFlags flags) { CONTRACT(OBJECTREF) { @@ -619,14 +615,12 @@ namespace struct { OBJECTREF implRef; - OBJECTREF targetTypeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = impl; - gc.targetTypeRef = targetType; ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); @@ -656,7 +650,7 @@ namespace COMPlusThrowHR(hr); // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags, &gc.targetTypeRef); + gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); @@ -930,21 +924,18 @@ namespace InteropLibImports struct { OBJECTREF implRef; - OBJECTREF targetTypeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = NULL; // Use the globally registered implementation. - gc.targetTypeRef = NULL; // No target type here. // Get wrapper for external object gc.objRef = GetOrCreateObjectForComInstanceInternal( gc.implRef, externalComObject, - externalObjectFlags, - gc.targetTypeRef); + externalObjectFlags); // Get wrapper for managed object *trackerTarget = GetOrCreateComInterfaceForObjectInternal( @@ -1075,7 +1066,6 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* ext, _In_ INT32 flags, - _In_ QCall::ObjectHandleOnStack targetTypeMaybe, _Inout_ QCall::ObjectHandleOnStack retValue) { QCALL_CONTRACT; @@ -1099,8 +1089,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal( ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), identity, - (CreateObjectFlags)flags, - ObjectToOBJECTREF(*targetTypeMaybe.m_ppObject)); + (CreateObjectFlags)flags); // Set the return value retValue.Set(newObj); diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index d826e3a7e96081..219c544e23366b 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -26,7 +26,6 @@ class ComWrappersNative _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* externalComObject, _In_ INT32 flags, - _In_ QCall::ObjectHandleOnStack targetTypeMaybe, _Inout_ QCall::ObjectHandleOnStack retValue); public: // Lifetime management for COM Wrappers diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 305cb69024409c..d754a95b6a996b 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -197,7 +197,7 @@ DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v)) DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) -DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_Type_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS) C(TYPE), j)) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS), j)) DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index cafbd34424027f..510d9e05060afe 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -463,7 +463,7 @@ DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) -DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_Type_RetObj) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj) DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid) #endif //FEATURE_COMINTEROP diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index b55817f422cb99..c6a37837a19d47 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -195,7 +195,7 @@ public bool DefaultReleaseObjects return entryRaw; } - protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flag, Type? targetType) + protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) { Assert.AreNotEqual(agileObj, IntPtr.Zero); @@ -271,7 +271,7 @@ static void ValidateRuntimeTrackerScenario() IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a managed wrapper for the native object. - var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); @@ -323,14 +323,14 @@ static void ValidateCreateObjectCachingScenario() // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); - var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance, null); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); Assert.AreNotEqual(trackerObj1, trackerObj3); } @@ -407,7 +407,7 @@ public enum FailureMode } - protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags, Type? targetType) + protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) { switch (CreateObjectMode) { @@ -451,13 +451,13 @@ static void ValidateBadComWrapperImpl() () => { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); }); try { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); } catch (Exception e) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index d42b3a0a093bb3..868221095457b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -49,12 +49,12 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { throw new PlatformNotSupportedException(); } - protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); + protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(IEnumerable objects) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 6914e6ad894d92..d6ab32ca603ae9 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1012,8 +1012,8 @@ public struct ComInterfaceDispatch } public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, Type? targetType) { throw null; } - protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags, Type? targetType); + public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } + protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterAsGlobalInstance() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } From 33194f9151f16a447d1ddd2823b03cfffcc790e9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 24 Feb 2020 12:17:42 -0800 Subject: [PATCH 67/83] Update GetOrCreateObjectForComInstance() to accept 'object?' as the defined wrapper. This will indicate to skip calling CallObject(). --- .../Runtime/InteropServices/ComWrappers.cs | 24 +++++++++---- src/coreclr/src/vm/interoplibinterface.cpp | 35 ++++++++++++++----- src/coreclr/src/vm/interoplibinterface.h | 1 + .../src/Interop/COM/ComWrappers/Program.cs | 16 ++++----- .../ComWrappers.PlatformNotSupported.cs | 4 +-- .../ref/System.Runtime.InteropServices.cs | 2 +- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 489da6ab692e9b..f828619eda61a5 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -38,7 +38,7 @@ public enum CreateComInterfaceFlags } /// - /// Enumeration of flags for . + /// Enumeration of flags for . /// [Flags] public enum CreateObjectFlags @@ -139,6 +139,8 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// /// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been /// allocated with the API. + /// + /// If the interface entries cannot be created and null is returned, the call to will throw a . /// protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); @@ -151,21 +153,29 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// /// Object to import for usage into the .NET runtime. /// Flags used to describe the external object. + /// An optional to be used as the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + /// + /// Providing a instance means + /// will not be called. + /// + /// If the instance already has an associated external object a will be thrown. + /// + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) { if (externalComObject == IntPtr.Zero) throw new ArgumentNullException(nameof(externalComObject)); ComWrappers impl = this; + object? wrapperLocal = wrapper; object? retValue = null; - GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref retValue)); + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperLocal), ObjectHandleOnStack.Create(ref retValue)); return retValue!; } [DllImport(RuntimeHelpers.QCall)] - private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack retValue); + private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue); /// /// Create a managed object for the object pointed at by respecting the values of . @@ -177,12 +187,12 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. /// - /// If the object cannot be created and null is returned, the call to will throw a . + /// If the object cannot be created and null is returned, the call to will throw a . /// - protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) + internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags); /// diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index f417f1fd489b77..f2c992059a6ed0 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -598,7 +598,8 @@ namespace OBJECTREF GetOrCreateObjectForComInstanceInternal( _In_opt_ OBJECTREF impl, _In_ IUnknown* identity, - _In_ CreateObjectFlags flags) + _In_ CreateObjectFlags flags, + _In_opt_ OBJECTREF wrapperMaybe) { CONTRACT(OBJECTREF) { @@ -615,12 +616,14 @@ namespace struct { OBJECTREF implRef; + OBJECTREF wrapperMaybeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = impl; + gc.wrapperMaybeRef = wrapperMaybe; ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance(); @@ -639,7 +642,7 @@ namespace } else { - // Create context and IAgileReference instance for the possibly new external COM object. + // Create context and IAgileReference instance for the possibly new external object. ExternalWrapperResultHolder resultHolder; hr = InteropLib::Com::CreateWrapperForExternal( identity, @@ -649,10 +652,16 @@ namespace if (FAILED(hr)) COMPlusThrowHR(hr); - // Call the implementation to create an external object wrapper. - gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); + // The user could have supplied a wrapper so assign that now. + gc.objRef = gc.wrapperMaybeRef; + + // If the wrapper hasn't been set yet, call the implementation to create one. if (gc.objRef == NULL) - COMPlusThrow(kArgumentNullException); + { + gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); + if (gc.objRef == NULL) + COMPlusThrow(kArgumentNullException); + } // Construct the new context with the object details. DWORD flags = (resultHolder.Result.FromTrackerRuntime @@ -687,7 +696,12 @@ namespace SyncBlock* syncBlock = gc.objRef->GetSyncBlock(); InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo(); _ASSERTE(syncBlock->IsPrecious()); - (void)interopInfo->TrySetExternalComObjectContext((void**)extObjCxt); + + // Since the caller has the option of providing a wrapper, it is + // possible the supplied wrapper already has an associated external + // object and an object can only be associated with one external object. + if (!interopInfo->TrySetExternalComObjectContext((void**)extObjCxt)) + COMPlusThrow(kNotSupportedException); // Detach from the holder to avoid cleanup. (void)resultHolder.DetachContext(); @@ -924,18 +938,21 @@ namespace InteropLibImports struct { OBJECTREF implRef; + OBJECTREF wrapperMaybeRef; OBJECTREF objRef; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.implRef = NULL; // Use the globally registered implementation. + gc.wrapperMaybeRef = NULL; // No supplied wrapper here. // Get wrapper for external object gc.objRef = GetOrCreateObjectForComInstanceInternal( gc.implRef, externalComObject, - externalObjectFlags); + externalObjectFlags, + gc.wrapperMaybeRef); // Get wrapper for managed object *trackerTarget = GetOrCreateComInterfaceForObjectInternal( @@ -1066,6 +1083,7 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* ext, _In_ INT32 flags, + _In_ QCall::ObjectHandleOnStack wrapperMaybe, _Inout_ QCall::ObjectHandleOnStack retValue) { QCALL_CONTRACT; @@ -1089,7 +1107,8 @@ void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance( OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal( ObjectToOBJECTREF(*comWrappersImpl.m_ppObject), identity, - (CreateObjectFlags)flags); + (CreateObjectFlags)flags, + ObjectToOBJECTREF(*wrapperMaybe.m_ppObject)); // Set the return value retValue.Set(newObj); diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 219c544e23366b..2ed54cfc907069 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -26,6 +26,7 @@ class ComWrappersNative _In_ QCall::ObjectHandleOnStack comWrappersImpl, _In_ void* externalComObject, _In_ INT32 flags, + _In_ QCall::ObjectHandleOnStack wrapperMaybe, _Inout_ QCall::ObjectHandleOnStack retValue); public: // Lifetime management for COM Wrappers diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index c6a37837a19d47..23db6f17277934 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -195,7 +195,7 @@ public bool DefaultReleaseObjects return entryRaw; } - protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flag) { Assert.AreNotEqual(agileObj, IntPtr.Zero); @@ -271,7 +271,7 @@ static void ValidateRuntimeTrackerScenario() IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a managed wrapper for the native object. - var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); @@ -323,14 +323,14 @@ static void ValidateCreateObjectCachingScenario() // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); - var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance, null); Assert.AreNotEqual(trackerObj1, trackerObj3); } @@ -407,7 +407,7 @@ public enum FailureMode } - protected override object CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) { switch (CreateObjectMode) { @@ -451,13 +451,13 @@ static void ValidateBadComWrapperImpl() () => { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); }); try { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); } catch (Exception e) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index 868221095457b9..736e219d45678f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -49,12 +49,12 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) { throw new PlatformNotSupportedException(); } - protected abstract object CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(IEnumerable objects) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index d6ab32ca603ae9..e540fc384147ae 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1012,7 +1012,7 @@ public struct ComInterfaceDispatch } public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } + public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) { throw null; } protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterAsGlobalInstance() { } From 7147244c0c2129e3fe2e11a10e121df7a3b912a2 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 24 Feb 2020 12:42:58 -0800 Subject: [PATCH 68/83] Validate failure for reuse of wrapper for external object. --- .../src/Interop/COM/ComWrappers/Program.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 23db6f17277934..589bb518c15ebe 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -271,7 +271,7 @@ static void ValidateRuntimeTrackerScenario() IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a managed wrapper for the native object. - var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); @@ -323,15 +323,24 @@ static void ValidateCreateObjectCachingScenario() // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); - var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, null); + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance, null); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); Assert.AreNotEqual(trackerObj1, trackerObj3); + + // Validate reuse of a wrapper fails. + IntPtr trackerObjRaw2 = MockReferenceTrackerRuntime.CreateTrackerObject(); + Assert.Throws( + () => + { + cw.GetOrCreateObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, trackerObj3); + }); + Marshal.Release(trackerObjRaw2); } static void ValidateIUnknownImpls() @@ -451,13 +460,13 @@ static void ValidateBadComWrapperImpl() () => { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); }); try { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; - wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); + wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); } catch (Exception e) { From 1d87df41260764826c4ac38699c9a20853cd1730 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 24 Feb 2020 13:39:29 -0800 Subject: [PATCH 69/83] Fix bug in failure case of attempting to reuse a wrapper. --- src/coreclr/src/vm/interoplibinterface.cpp | 7 + .../src/Interop/COM/ComWrappers/Program.cs | 203 ++++++++++-------- 2 files changed, 121 insertions(+), 89 deletions(-) diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index f2c992059a6ed0..6f595f14aa9bed 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -701,7 +701,14 @@ namespace // possible the supplied wrapper already has an associated external // object and an object can only be associated with one external object. if (!interopInfo->TrySetExternalComObjectContext((void**)extObjCxt)) + { + // Failed to set the context; one must already exist. + // Remove from the cache above as well. + ExtObjCxtCache::LockHolder lock(cache); + cache->Remove(resultHolder.GetContext()); + COMPlusThrow(kNotSupportedException); + } // Detach from the holder to avoid cleanup. (void)resultHolder.DetachContext(); diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 589bb518c15ebe..718579806e1cbc 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -261,84 +261,55 @@ static void ValidateComInterfaceCreation() Assert.AreEqual(count, 0); } - static void ValidateRuntimeTrackerScenario() + static void ValidateCreateObjectCachingScenario() { - Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}..."); + Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - // Create a managed wrapper for the native object. - var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var testWrapperIds = new List(); - for (int i = 0; i < 1000; ++i) - { - // Create a native wrapper for the managed object. - IntPtr testWrapper = cw.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.TrackerSupport); - - // Pass the managed object to the native object. - int id = trackerObj.AddObjectRef(testWrapper); - - // Retain the managed object wrapper ptr. - testWrapperIds.Add(id); - } - - Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); - - GC.Collect(); - GC.Collect(); - GC.Collect(); - GC.Collect(); - GC.Collect(); - - Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); - - // Remove the managed object ref from the native object. - foreach (int id in testWrapperIds) - { - trackerObj.DropObjectRef(id); - } - - testWrapperIds.Clear(); - - GC.Collect(); - GC.Collect(); - GC.Collect(); - GC.Collect(); - GC.Collect(); + var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); + Assert.AreNotEqual(trackerObj1, trackerObj3); } - static void ValidateCreateObjectCachingScenario() + static void ValidatePrecreatedExternalWrapper() { - Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}..."); + Console.WriteLine($"Running {nameof(ValidatePrecreatedExternalWrapper)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); - var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); - var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); - Assert.AreEqual(trackerObj1, trackerObj2); + // Manually create a wrapper + var iid = typeof(ITrackerObject).GUID; + IntPtr iTestComObject; + int hr = Marshal.QueryInterface(trackerObjRaw, ref iid, out iTestComObject); + Assert.AreEqual(hr, 0); + var nativeWrapper = new ITrackerObjectWrapper(iTestComObject); + + // Request wrapper, but supply the wrapper. + var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper); + Assert.AreEqual(nativeWrapper, nativeWrapper2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); - var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); - Assert.AreNotEqual(trackerObj1, trackerObj3); - // Validate reuse of a wrapper fails. IntPtr trackerObjRaw2 = MockReferenceTrackerRuntime.CreateTrackerObject(); Assert.Throws( () => { - cw.GetOrCreateObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, trackerObj3); + cw.GetOrCreateObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2); }); Marshal.Release(trackerObjRaw2); } @@ -346,45 +317,6 @@ static void ValidateCreateObjectCachingScenario() static void ValidateIUnknownImpls() => TestComWrappers.ValidateIUnknownImpls(); - static void ValidateGlobalInstanceScenarios() - { - Console.WriteLine($"Running {nameof(ValidateGlobalInstanceScenarios)}..."); - Console.WriteLine($"Validate RegisterAsGlobalInstance()..."); - - var wrappers1 = TestComWrappers.Global; - wrappers1.RegisterAsGlobalInstance(); - - Assert.Throws( - () => - { - wrappers1.RegisterAsGlobalInstance(); - }, "Should not be able to re-register for global ComWrappers"); - - var wrappers2 = new TestComWrappers(); - Assert.Throws( - () => - { - wrappers2.RegisterAsGlobalInstance(); - }, "Should not be able to reset for global ComWrappers"); - - Console.WriteLine($"Validate NotifyEndOfReferenceTrackingOnThread()..."); - - int hr; - var cw = TestComWrappers.Global; - - // Trigger the thread lifetime end API and verify the default behavior. - hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); - Assert.AreEqual(unchecked((int)0x80004001), hr); - - cw.DefaultReleaseObjects = false; - // Trigger the thread lifetime end API and verify the customizable behavior. - hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); - Assert.AreEqual(0, hr); - - // Reset the global wrapper state. - cw.DefaultReleaseObjects = true; - } - class BadComWrappers : ComWrappers { public enum FailureMode @@ -476,15 +408,108 @@ static void ValidateBadComWrapperImpl() Marshal.Release(trackerObjRaw); } + static void ValidateRuntimeTrackerScenario() + { + Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}..."); + + var cw = new TestComWrappers(); + + // Get an object from a tracker runtime. + IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); + + // Create a managed wrapper for the native object. + var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); + + // Ownership has been transferred to the wrapper. + Marshal.Release(trackerObjRaw); + + var testWrapperIds = new List(); + for (int i = 0; i < 1000; ++i) + { + // Create a native wrapper for the managed object. + IntPtr testWrapper = cw.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.TrackerSupport); + + // Pass the managed object to the native object. + int id = trackerObj.AddObjectRef(testWrapper); + + // Retain the managed object wrapper ptr. + testWrapperIds.Add(id); + } + + Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); + + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + + Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); + + // Remove the managed object ref from the native object. + foreach (int id in testWrapperIds) + { + trackerObj.DropObjectRef(id); + } + + testWrapperIds.Clear(); + + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + } + + static void ValidateGlobalInstanceScenarios() + { + Console.WriteLine($"Running {nameof(ValidateGlobalInstanceScenarios)}..."); + Console.WriteLine($"Validate RegisterAsGlobalInstance()..."); + + var wrappers1 = TestComWrappers.Global; + wrappers1.RegisterAsGlobalInstance(); + + Assert.Throws( + () => + { + wrappers1.RegisterAsGlobalInstance(); + }, "Should not be able to re-register for global ComWrappers"); + + var wrappers2 = new TestComWrappers(); + Assert.Throws( + () => + { + wrappers2.RegisterAsGlobalInstance(); + }, "Should not be able to reset for global ComWrappers"); + + Console.WriteLine($"Validate NotifyEndOfReferenceTrackingOnThread()..."); + + int hr; + var cw = TestComWrappers.Global; + + // Trigger the thread lifetime end API and verify the default behavior. + hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); + Assert.AreEqual(unchecked((int)0x80004001), hr); + + cw.DefaultReleaseObjects = false; + // Trigger the thread lifetime end API and verify the customizable behavior. + hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); + Assert.AreEqual(0, hr); + + // Reset the global wrapper state. + cw.DefaultReleaseObjects = true; + } + static int Main(string[] doNotUse) { try { ValidateComInterfaceCreation(); - ValidateRuntimeTrackerScenario(); ValidateCreateObjectCachingScenario(); + ValidatePrecreatedExternalWrapper(); ValidateIUnknownImpls(); ValidateBadComWrapperImpl(); + ValidateRuntimeTrackerScenario(); // Perform all global impacting test scenarios last to // avoid polluting non-global tests. From 17b54040a706162025ef1954bd2a067f7c0b76f5 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 24 Feb 2020 14:43:12 -0800 Subject: [PATCH 70/83] Remove IAgileReference usage. --- .../Runtime/InteropServices/ComWrappers.cs | 11 ++-- src/coreclr/src/interop/CMakeLists.txt | 1 - src/coreclr/src/interop/agilereferences.cpp | 50 ------------------- src/coreclr/src/interop/comwrappers.hpp | 9 ---- src/coreclr/src/interop/inc/interoplib.h | 3 -- src/coreclr/src/interop/interoplib.cpp | 5 -- src/coreclr/src/vm/interoplibinterface.cpp | 12 ++--- src/coreclr/src/vm/metasig.h | 2 +- src/coreclr/src/vm/mscorlib.h | 2 +- .../src/Interop/COM/ComWrappers/Program.cs | 6 +-- .../ComWrappers.PlatformNotSupported.cs | 2 +- .../ref/System.Runtime.InteropServices.cs | 2 +- 12 files changed, 14 insertions(+), 91 deletions(-) delete mode 100644 src/coreclr/src/interop/agilereferences.cpp diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index f828619eda61a5..111ba2241e3a83 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -178,22 +178,19 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue); /// - /// Create a managed object for the object pointed at by respecting the values of . + /// Create a managed object for the object pointed at by respecting the values of . /// /// Object to import for usage into the .NET runtime. - /// IAgileReference pointing at the object to import into the .NET runtime. /// Flags used to describe the external object. /// Returns a managed object associated with the supplied external COM object. /// - /// The is an IAgileReference instance. This type should be used to ensure the associated external object, if not known to be free-threaded, is released from the correct COM apartment. - /// /// If the object cannot be created and null is returned, the call to will throw a . /// - protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags); // Call to execute the abstract instance function - internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags) - => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, agileObjectRef, flags); + internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags) + => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, flags); /// /// Called when a request is made for a collection of objects to be released. diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt index 47a3e5970e523e..d7eaa1b04ae63d 100644 --- a/src/coreclr/src/interop/CMakeLists.txt +++ b/src/coreclr/src/interop/CMakeLists.txt @@ -22,7 +22,6 @@ set(INTEROP_HEADERS if (WIN32) list(APPEND INTEROP_SOURCES ${INTEROP_HEADERS} - agilereferences.cpp comwrappers.cpp comwrappers.hpp trackerobjectmanager.cpp diff --git a/src/coreclr/src/interop/agilereferences.cpp b/src/coreclr/src/interop/agilereferences.cpp deleted file mode 100644 index 58937ba2a0f307..00000000000000 --- a/src/coreclr/src/interop/agilereferences.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// Runtime headers -#include - -#include "comwrappers.hpp" - -// Forward declare the Win32 API. -enum AgileReferenceOptions -{ - AGILEREFERENCE_DEFAULT = 0, - AGILEREFERENCE_DELAYEDMARSHAL = 1, -}; - -EXTERN_C HRESULT STDAPICALLTYPE RoGetAgileReference( - _In_ enum AgileReferenceOptions options, - _In_ REFIID riid, - _In_ IUnknown* pUnk, - _COM_Outptr_ IAgileReference** ppAgileReference - ); - -namespace -{ - // Global function pointer for RoGetAgileReference - decltype(RoGetAgileReference)* fpRoGetAgileReference; -} - -template<> -HRESULT CreateAgileReference( - _In_ IUnknown* object, - _Outptr_ IAgileReference** agileReference) -{ - _ASSERTE(object != nullptr && agileReference != nullptr); - - // If the pointer isn't set, then attempt to load it in process. - if (fpRoGetAgileReference == nullptr) - { - HMODULE hmod = WszLoadLibrary(W("ole32.dll")); - if (hmod != nullptr) - fpRoGetAgileReference = (decltype(RoGetAgileReference)*)::GetProcAddress(hmod, "RoGetAgileReference"); - - // Could't find binary or export. Either way, the OS version is too old. - if (fpRoGetAgileReference == nullptr) - return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); - } - - return fpRoGetAgileReference(AGILEREFERENCE_DEFAULT, __uuidof(object), object, agileReference); -} diff --git a/src/coreclr/src/interop/comwrappers.hpp b/src/coreclr/src/interop/comwrappers.hpp index ed048023e1211a..d2337746e39b8c 100644 --- a/src/coreclr/src/interop/comwrappers.hpp +++ b/src/coreclr/src/interop/comwrappers.hpp @@ -152,15 +152,6 @@ class NativeObjectWrapperContext void DisconnectTracker() noexcept; }; -// API for creating an IAgileReference instance to a supplied IUnknown instance. -// See https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-rogetagilereference -// -// N.B. Using a template so that callers are required to provide an explicit IUnknown instance. -template -HRESULT CreateAgileReference( - _In_ T* object, - _Outptr_ IAgileReference** agileReference); - // Manage native object wrappers that support IReferenceTracker. class TrackerObjectManager { diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h index e68f1857cd3398..df59b8b90a584e 100644 --- a/src/coreclr/src/interop/inc/interoplib.h +++ b/src/coreclr/src/interop/inc/interoplib.h @@ -50,9 +50,6 @@ namespace InteropLib // The returned context memory is guaranteed to be initialized to zero. void* Context; - // IAgileReference instance. - IUnknown* AgileRef; - // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ // for details. bool FromTrackerRuntime; diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp index 3d1b7079ccd22e..8283024ad5d4a4 100644 --- a/src/coreclr/src/interop/interoplib.cpp +++ b/src/coreclr/src/interop/interoplib.cpp @@ -99,15 +99,10 @@ namespace InteropLib HRESULT hr; - // Attempt to create an agile reference first. - ComHolder reference; - RETURN_IF_FAILED(CreateAgileReference(external, &reference)); - NativeObjectWrapperContext* wrapperContext; RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext)); result->Context = wrapperContext->GetRuntimeContext(); - result->AgileRef = reference.Detach(); result->FromTrackerRuntime = (wrapperContext->GetReferenceTracker() != nullptr); return S_OK; } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 6f595f14aa9bed..8a399d5a588b58 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -109,8 +109,6 @@ namespace { if (Result.Context != NULL) InteropLib::Com::DestroyWrapperForExternal(Result.Context); - if (Result.AgileRef != NULL) - (void)Result.AgileRef->Release(); } InteropLib::Com::ExternalWrapperResult* operator&() { @@ -437,7 +435,6 @@ namespace OBJECTREF CallGetObject( _In_ OBJECTREF* implPROTECTED, _In_ IUnknown* externalComObject, - _In_ IUnknown* agileObjectRef, _In_ INT32 flags) { CONTRACTL @@ -452,11 +449,10 @@ namespace OBJECTREF retObjRef; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 3); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(agileObjectRef); - args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args); return retObjRef; @@ -642,7 +638,7 @@ namespace } else { - // Create context and IAgileReference instance for the possibly new external object. + // Create context instance for the possibly new external object. ExternalWrapperResultHolder resultHolder; hr = InteropLib::Com::CreateWrapperForExternal( identity, @@ -658,7 +654,7 @@ namespace // If the wrapper hasn't been set yet, call the implementation to create one. if (gc.objRef == NULL) { - gc.objRef = CallGetObject(&gc.implRef, identity, resultHolder.Result.AgileRef, flags); + gc.objRef = CallGetObject(&gc.implRef, identity, flags); if (gc.objRef == NULL) COMPlusThrow(kArgumentNullException); } diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index d754a95b6a996b..e7830468cfa589 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -197,7 +197,7 @@ DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v)) DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v)) DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION))) DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v))) -DEFINE_METASIG_T(SM(ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I I g(CREATEOBJECTFLAGS), j)) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j)) DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v)) #endif // FEATURE_COMINTEROP DEFINE_METASIG(SM(Int_RetVoid, i, v)) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 510d9e05060afe..12ec6d8929123a 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -463,7 +463,7 @@ DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid) -DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_IntPtr_CreateFlags_RetObj) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_RetObj) DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid) #endif //FEATURE_COMINTEROP diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 718579806e1cbc..ce8a32e5be047a 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -195,10 +195,8 @@ public bool DefaultReleaseObjects return entryRaw; } - protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flag) + protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flag) { - Assert.AreNotEqual(agileObj, IntPtr.Zero); - var iid = typeof(ITrackerObject).GUID; IntPtr iTestComObject; int hr = Marshal.QueryInterface(externalComObject, ref iid, out iTestComObject); @@ -348,7 +346,7 @@ public enum FailureMode } - protected override object? CreateObject(IntPtr externalComObject, IntPtr agileObj, CreateObjectFlags flags) + protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) { switch (CreateObjectMode) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index 736e219d45678f..16f52677b4da9c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -54,7 +54,7 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb throw new PlatformNotSupportedException(); } - protected abstract object? CreateObject(IntPtr externalComObject, IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags); protected virtual void ReleaseObjects(IEnumerable objects) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index e540fc384147ae..9a3555daaad0c8 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1013,7 +1013,7 @@ public struct ComInterfaceDispatch public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) { throw null; } - protected abstract object CreateObject(System.IntPtr externalComObject, System.IntPtr agileObjectRef, CreateObjectFlags flags); + protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags); protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } public void RegisterAsGlobalInstance() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } From 9cc5cccb9746d413011707b02fe9d77329aa8b0f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 15:18:47 -0800 Subject: [PATCH 71/83] Apply API review comments. --- .../Runtime/InteropServices/ComWrappers.cs | 73 ++++++++++--------- .../ComWrappers.PlatformNotSupported.cs | 8 +- .../ref/System.Runtime.InteropServices.cs | 7 +- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 111ba2241e3a83..43fa6ee339ad2d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -38,7 +38,7 @@ public enum CreateComInterfaceFlags } /// - /// Enumeration of flags for . + /// Enumeration of flags for . /// [Flags] public enum CreateObjectFlags @@ -83,17 +83,17 @@ public struct ComInterfaceEntry /// public struct ComInterfaceDispatch { - public IntPtr vftbl; + public IntPtr Vtable; /// - /// Given a from a generated VTable, convert to the target type. + /// Given a from a generated Vtable, convert to the target type. /// /// Desired type. - /// Pointer supplied to VTable function entry. + /// Pointer supplied to Vtable function entry. /// Instance of type associated with dispatched function call. public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class { - // See the CCW dispatch section in the runtime for details on the masking below. + // See the dispatch section in the runtime for details on the masking below. const long DispatchThisPtrMask = ~0xfL; var comInstance = *(ComInterfaceInstance**)(((long)dispatchPtr) & DispatchThisPtrMask); @@ -112,9 +112,9 @@ private struct ComInterfaceInstance private static ComWrappers? s_globalInstance; /// - /// Create an COM representation of the supplied object that can be passed to an non-managed environment. + /// Create a COM representation of the supplied object that can be passed to a non-managed environment. /// - /// A GC Handle to the managed object to expose outside the .NET runtime. + /// The managed object to expose outside the .NET runtime. /// Flags used to configure the generated interface. /// The generated COM interface that can be passed outside the .NET runtime. public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) @@ -130,10 +130,10 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa private static extern IntPtr GetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack comWrappersImpl, ObjectHandleOnStack instance, CreateComInterfaceFlags flags); /// - /// Compute the desired VTables for respecting the values of . + /// Compute the desired Vtable for respecting the values of . /// - /// Target of the returned VTables. - /// Flags used to compute VTables. + /// Target of the returned Vtables. + /// Flags used to compute Vtables. /// The number of elements contained in the returned memory. /// pointer containing memory for all COM interface entries. /// @@ -153,25 +153,10 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// /// Object to import for usage into the .NET runtime. /// Flags used to describe the external object. - /// An optional to be used as the wrapper for the external object /// Returns a managed object associated with the supplied external COM object. - /// - /// Providing a instance means - /// will not be called. - /// - /// If the instance already has an associated external object a will be thrown. - /// - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { - if (externalComObject == IntPtr.Zero) - throw new ArgumentNullException(nameof(externalComObject)); - - ComWrappers impl = this; - object? wrapperLocal = wrapper; - object? retValue = null; - GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperLocal), ObjectHandleOnStack.Create(ref retValue)); - - return retValue!; + return GetOrCreateObjectForComInstanceEx(externalComObject, flags, null); } [DllImport(RuntimeHelpers.QCall)] @@ -184,7 +169,7 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// Flags used to describe the external object. /// Returns a managed object associated with the supplied external COM object. /// - /// If the object cannot be created and null is returned, the call to will throw a . + /// If the object cannot be created and null is returned, the call to will throw a . /// protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags); @@ -193,17 +178,39 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, flags); /// - /// Called when a request is made for a collection of objects to be released. + /// Get the currently registered managed object or uses the supplied managed object and registers it. /// - /// Collection of objects to release. + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// The to be used as the wrapper for the external object + /// Returns a managed object associated with the supplied external COM object. /// - /// The default implementation of this function throws . + /// If the instance already has an associated external object a will be thrown. /// - protected virtual void ReleaseObjects(IEnumerable objects) + public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { - throw new NotImplementedException(); + return GetOrCreateObjectForComInstanceEx(externalComObject, flags, wrapper); } + private object GetOrCreateObjectForComInstanceEx(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper) + { + if (externalComObject == IntPtr.Zero) + throw new ArgumentNullException(nameof(externalComObject)); + + ComWrappers impl = this; + object? wrapperLocal = wrapper; + object? retValue = null; + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperLocal), ObjectHandleOnStack.Create(ref retValue)); + + return retValue!; + } + + /// + /// Called when a request is made for a collection of objects to be released outside of normal object or COM interface lifetime. + /// + /// Collection of objects to release. + protected abstract void ReleaseObjects(IEnumerable objects); + // Call to execute the virtual instance function internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects) => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs index 16f52677b4da9c..77e8b35baae469 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs @@ -34,7 +34,7 @@ public struct ComInterfaceEntry public struct ComInterfaceDispatch { - public IntPtr vftbl; + public IntPtr Vtable; public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class { @@ -49,18 +49,20 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { throw new PlatformNotSupportedException(); } protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags); - protected virtual void ReleaseObjects(IEnumerable objects) + public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { throw new PlatformNotSupportedException(); } + protected abstract void ReleaseObjects(IEnumerable objects); + public void RegisterAsGlobalInstance() { throw new PlatformNotSupportedException(); diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 9a3555daaad0c8..92ddefd113e4af 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1007,14 +1007,15 @@ public struct ComInterfaceEntry } public struct ComInterfaceDispatch { - public System.IntPtr vftbl; + public System.IntPtr Vtable; public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class { throw null; } } public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; } protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); - public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object? wrapper = null) { throw null; } + public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; } protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags); - protected virtual void ReleaseObjects(System.Collections.IEnumerable objects) { throw null; } + public object GetOrRegisterObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { throw null; } + protected abstract void ReleaseObjects(System.Collections.IEnumerable objects); public void RegisterAsGlobalInstance() { } protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; } } From 1ae8587169e4602deea9cea4c90b6f9134365e84 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 15:19:14 -0800 Subject: [PATCH 72/83] Update test to align with API changes. --- .../src/Interop/COM/ComWrappers/Program.cs | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index ce8a32e5be047a..54c27743122f8b 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -155,16 +155,6 @@ class TestComWrappers : ComWrappers { public static readonly TestComWrappers Global = new TestComWrappers(); - public TestComWrappers() - { - DefaultReleaseObjects = true; - } - - public bool DefaultReleaseObjects - { - get; set; - } - protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { Assert.IsTrue(obj is Test); @@ -205,13 +195,11 @@ public bool DefaultReleaseObjects return new ITrackerObjectWrapper(iTestComObject); } + public const int ReleaseObjectsCallAck = unchecked((int)-1); + protected override void ReleaseObjects(IEnumerable objects) { - if (DefaultReleaseObjects) - { - base.ReleaseObjects(objects); - Assert.Fail("Default implementation should throw exception"); - } + throw new Exception() { HResult = ReleaseObjectsCallAck }; } public static void ValidateIUnknownImpls() @@ -296,7 +284,7 @@ static void ValidatePrecreatedExternalWrapper() var nativeWrapper = new ITrackerObjectWrapper(iTestComObject); // Request wrapper, but supply the wrapper. - var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper); + var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper); Assert.AreEqual(nativeWrapper, nativeWrapper2); // Ownership has been transferred to the wrapper. @@ -307,7 +295,7 @@ static void ValidatePrecreatedExternalWrapper() Assert.Throws( () => { - cw.GetOrCreateObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2); + cw.GetOrRegisterObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2); }); Marshal.Release(trackerObjRaw2); } @@ -343,7 +331,6 @@ public enum FailureMode Assert.Fail("Invalid failure mode"); throw new Exception("UNREACHABLE"); } - } protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) @@ -359,6 +346,11 @@ public enum FailureMode throw new Exception("UNREACHABLE"); } } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotSupportedException(); + } } static void ValidateBadComWrapperImpl() @@ -485,17 +477,9 @@ static void ValidateGlobalInstanceScenarios() int hr; var cw = TestComWrappers.Global; - // Trigger the thread lifetime end API and verify the default behavior. - hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); - Assert.AreEqual(unchecked((int)0x80004001), hr); - - cw.DefaultReleaseObjects = false; - // Trigger the thread lifetime end API and verify the customizable behavior. + // Trigger the thread lifetime end API and verify the callback occurs. hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread(); - Assert.AreEqual(0, hr); - - // Reset the global wrapper state. - cw.DefaultReleaseObjects = true; + Assert.AreEqual(TestComWrappers.ReleaseObjectsCallAck, hr); } static int Main(string[] doNotUse) From 96fa8ccf184b4a84dee6aa3fa1970ce4879f7473 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 15:59:25 -0800 Subject: [PATCH 73/83] Merge conflict. --- src/coreclr/src/inc/crsttypes.h | 216 ++++++++++++++++---------------- 1 file changed, 109 insertions(+), 107 deletions(-) diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index fa223e5fd17324..3638826f769d32 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -63,113 +63,114 @@ enum CrstType CrstException = 44, CrstExecuteManLock = 45, CrstExecuteManRangeLock = 46, - CrstFCall = 47, - CrstFriendAccessCache = 48, - CrstFuncPtrStubs = 49, - CrstFusionAppCtx = 50, - CrstGCCover = 51, - CrstGCMemoryPressure = 52, - CrstGlobalStrLiteralMap = 53, - CrstHandleTable = 54, - CrstHostAssemblyMap = 55, - CrstHostAssemblyMapAdd = 56, - CrstIbcProfile = 57, - CrstIJWFixupData = 58, - CrstIJWHash = 59, - CrstILStubGen = 60, - CrstInlineTrackingMap = 61, - CrstInstMethodHashTable = 62, - CrstInterfaceVTableMap = 63, - CrstInterop = 64, - CrstInteropData = 65, - CrstIOThreadpoolWorker = 66, - CrstIsJMCMethod = 67, - CrstISymUnmanagedReader = 68, - CrstJit = 69, - CrstJitGenericHandleCache = 70, - CrstJitInlineTrackingMap = 71, - CrstJitPerf = 72, - CrstJumpStubCache = 73, - CrstLeafLock = 74, - CrstListLock = 75, - CrstLoaderAllocator = 76, - CrstLoaderAllocatorReferences = 77, - CrstLoaderHeap = 78, - CrstMda = 79, - CrstMetadataTracker = 80, - CrstMethodDescBackpatchInfoTracker = 81, - CrstModIntPairList = 82, - CrstModule = 83, - CrstModuleFixup = 84, - CrstModuleLookupTable = 85, - CrstMulticoreJitHash = 86, - CrstMulticoreJitManager = 87, - CrstMUThunkHash = 88, - CrstNativeBinderInit = 89, - CrstNativeImageCache = 90, - CrstNativeImageEagerFixups = 91, - CrstNls = 92, - CrstNotifyGdb = 93, - CrstObjectList = 94, - CrstOnEventManager = 95, - CrstPatchEntryPoint = 96, - CrstPEImage = 97, - CrstPEImagePDBStream = 98, - CrstPendingTypeLoadEntry = 99, - CrstPinHandle = 100, - CrstPinnedByrefValidation = 101, - CrstProfilerGCRefDataFreeList = 102, - CrstProfilingAPIStatus = 103, - CrstPublisherCertificate = 104, - CrstRCWCache = 105, - CrstRCWCleanupList = 106, - CrstRCWRefCache = 107, - CrstReadyToRunEntryPointToMethodDescMap = 108, - CrstReDacl = 109, - CrstReflection = 110, - CrstReJITGlobalRequest = 111, - CrstRemoting = 112, - CrstRetThunkCache = 113, - CrstRWLock = 114, - CrstSavedExceptionInfo = 115, - CrstSaveModuleProfileData = 116, - CrstSecurityStackwalkCache = 117, - CrstSharedAssemblyCreate = 118, - CrstSigConvert = 119, - CrstSingleUseLock = 120, - CrstSpecialStatics = 121, - CrstSqmManager = 122, - CrstStackSampler = 123, - CrstStressLog = 124, - CrstStrongName = 125, - CrstStubCache = 126, - CrstStubDispatchCache = 127, - CrstStubUnwindInfoHeapSegments = 128, - CrstSyncBlockCache = 129, - CrstSyncHashLock = 130, - CrstSystemBaseDomain = 131, - CrstSystemDomain = 132, - CrstSystemDomainDelayedUnloadList = 133, - CrstThreadIdDispenser = 134, - CrstThreadpoolEventCache = 135, - CrstThreadpoolTimerQueue = 136, - CrstThreadpoolWaitThreads = 137, - CrstThreadpoolWorker = 138, - CrstThreadStaticDataHashTable = 139, - CrstThreadStore = 140, - CrstTieredCompilation = 141, - CrstTPMethodTable = 142, - CrstTypeEquivalenceMap = 143, - CrstTypeIDMap = 144, - CrstUMEntryThunkCache = 145, - CrstUMThunkHash = 146, - CrstUniqueStack = 147, - CrstUnresolvedClassLock = 148, - CrstUnwindInfoTableLock = 149, - CrstVSDIndirectionCellLock = 150, - CrstWinRTFactoryCache = 151, - CrstWrapperTemplate = 152, - kNumberOfCrstTypes = 153 + CrstExternalObjectContextCache = 47, + CrstFCall = 48, + CrstFriendAccessCache = 49, + CrstFuncPtrStubs = 50, + CrstFusionAppCtx = 51, + CrstGCCover = 52, + CrstGCMemoryPressure = 53, + CrstGlobalStrLiteralMap = 54, + CrstHandleTable = 55, + CrstHostAssemblyMap = 56, + CrstHostAssemblyMapAdd = 57, + CrstIbcProfile = 58, + CrstIJWFixupData = 59, + CrstIJWHash = 60, + CrstILStubGen = 61, + CrstInlineTrackingMap = 62, + CrstInstMethodHashTable = 63, + CrstInterfaceVTableMap = 64, + CrstInterop = 65, + CrstInteropData = 66, + CrstIOThreadpoolWorker = 67, + CrstIsJMCMethod = 68, + CrstISymUnmanagedReader = 69, + CrstJit = 70, + CrstJitGenericHandleCache = 71, + CrstJitInlineTrackingMap = 72, + CrstJitPerf = 73, + CrstJumpStubCache = 74, + CrstLeafLock = 75, + CrstListLock = 76, + CrstLoaderAllocator = 77, + CrstLoaderAllocatorReferences = 78, + CrstLoaderHeap = 79, + CrstMda = 80, + CrstMetadataTracker = 81, + CrstMethodDescBackpatchInfoTracker = 82, + CrstModIntPairList = 83, + CrstModule = 84, + CrstModuleFixup = 85, + CrstModuleLookupTable = 86, + CrstMulticoreJitHash = 87, + CrstMulticoreJitManager = 88, + CrstMUThunkHash = 89, + CrstNativeBinderInit = 90, + CrstNativeImageCache = 91, + CrstNativeImageEagerFixups = 92, + CrstNls = 93, + CrstNotifyGdb = 94, + CrstObjectList = 95, + CrstOnEventManager = 96, + CrstPatchEntryPoint = 97, + CrstPEImage = 98, + CrstPEImagePDBStream = 99, + CrstPendingTypeLoadEntry = 100, + CrstPinHandle = 101, + CrstPinnedByrefValidation = 102, + CrstProfilerGCRefDataFreeList = 103, + CrstProfilingAPIStatus = 104, + CrstPublisherCertificate = 105, + CrstRCWCache = 106, + CrstRCWCleanupList = 107, + CrstRCWRefCache = 108, + CrstReadyToRunEntryPointToMethodDescMap = 109, + CrstReDacl = 110, + CrstReflection = 111, + CrstReJITGlobalRequest = 112, + CrstRemoting = 113, + CrstRetThunkCache = 114, + CrstRWLock = 115, + CrstSavedExceptionInfo = 116, + CrstSaveModuleProfileData = 117, + CrstSecurityStackwalkCache = 118, + CrstSharedAssemblyCreate = 119, + CrstSigConvert = 120, + CrstSingleUseLock = 121, + CrstSpecialStatics = 122, + CrstSqmManager = 123, + CrstStackSampler = 124, + CrstStressLog = 125, + CrstStrongName = 126, + CrstStubCache = 127, + CrstStubDispatchCache = 128, + CrstStubUnwindInfoHeapSegments = 129, + CrstSyncBlockCache = 130, + CrstSyncHashLock = 131, + CrstSystemBaseDomain = 132, + CrstSystemDomain = 133, + CrstSystemDomainDelayedUnloadList = 134, + CrstThreadIdDispenser = 135, + CrstThreadpoolEventCache = 136, + CrstThreadpoolTimerQueue = 137, + CrstThreadpoolWaitThreads = 138, + CrstThreadpoolWorker = 139, + CrstThreadStaticDataHashTable = 140, + CrstThreadStore = 141, + CrstTieredCompilation = 142, + CrstTPMethodTable = 143, + CrstTypeEquivalenceMap = 144, + CrstTypeIDMap = 145, + CrstUMEntryThunkCache = 146, + CrstUMThunkHash = 147, + CrstUniqueStack = 148, + CrstUnresolvedClassLock = 149, + CrstUnwindInfoTableLock = 150, + CrstVSDIndirectionCellLock = 151, + CrstWinRTFactoryCache = 152, + CrstWrapperTemplate = 153, + kNumberOfCrstTypes = 154 }; #endif // __CRST_TYPES_INCLUDED @@ -227,6 +228,7 @@ int g_rgCrstLevelMap[] = 0, // CrstException 7, // CrstExecuteManLock 0, // CrstExecuteManRangeLock + 0, // CrstExternalObjectContextCache 3, // CrstFCall 7, // CrstFriendAccessCache 7, // CrstFuncPtrStubs From fc2f20ad68f70207162817b00b35ca654d8de5f6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 15:59:34 -0800 Subject: [PATCH 74/83] Review feedback. --- .../Runtime/InteropServices/ComWrappers.cs | 19 +++++++++++-------- .../src/Interop/COM/ComWrappers/Program.cs | 9 ++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index 43fa6ee339ad2d..f5d0e06b016a33 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -156,12 +156,9 @@ public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfa /// Returns a managed object associated with the supplied external COM object. public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) { - return GetOrCreateObjectForComInstanceEx(externalComObject, flags, null); + return GetOrCreateObjectForComInstanceInternal(externalComObject, flags, null); } - [DllImport(RuntimeHelpers.QCall)] - private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue); - /// /// Create a managed object for the object pointed at by respecting the values of . /// @@ -189,22 +186,28 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { - return GetOrCreateObjectForComInstanceEx(externalComObject, flags, wrapper); + if (wrapper == null) + throw new ArgumentNullException(nameof(externalComObject)); + + return GetOrCreateObjectForComInstanceInternal(externalComObject, flags, wrapper); } - private object GetOrCreateObjectForComInstanceEx(IntPtr externalComObject, CreateObjectFlags flags, object? wrapper) + private object GetOrCreateObjectForComInstanceInternal(IntPtr externalComObject, CreateObjectFlags flags, object? wrapperMaybe) { if (externalComObject == IntPtr.Zero) throw new ArgumentNullException(nameof(externalComObject)); ComWrappers impl = this; - object? wrapperLocal = wrapper; + object? wrapperMaybeLocal = wrapperMaybe; object? retValue = null; - GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperLocal), ObjectHandleOnStack.Create(ref retValue)); + GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperMaybeLocal), ObjectHandleOnStack.Create(ref retValue)); return retValue!; } + [DllImport(RuntimeHelpers.QCall)] + private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue); + /// /// Called when a request is made for a collection of objects to be released outside of normal object or COM interface lifetime. /// diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs index 54c27743122f8b..e85f87a4b82697 100644 --- a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs @@ -283,7 +283,7 @@ static void ValidatePrecreatedExternalWrapper() Assert.AreEqual(hr, 0); var nativeWrapper = new ITrackerObjectWrapper(iTestComObject); - // Request wrapper, but supply the wrapper. + // Register wrapper, but supply the wrapper. var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper); Assert.AreEqual(nativeWrapper, nativeWrapper2); @@ -298,6 +298,13 @@ static void ValidatePrecreatedExternalWrapper() cw.GetOrRegisterObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2); }); Marshal.Release(trackerObjRaw2); + + // Validate passing null wrapper fails. + Assert.Throws( + () => + { + cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); + }); } static void ValidateIUnknownImpls() From 9c4ecd5f36ce600b4b2cec474a0b879ec8549daf Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 16:48:02 -0800 Subject: [PATCH 75/83] Add stub for AllocateTypeAssociatedMemory() in Mono build. --- .../System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 79a6a6902593bb..821ce696cdff68 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -99,6 +99,11 @@ public static void RunModuleConstructor (ModuleHandle module) RunModuleConstructor (module.Value); } + public static IntPtr AllocateTypeAssociatedMemory (Type type, int size) + { + throw new PlatformNotSupported(); + } + [Intrinsic] public static bool IsReferenceOrContainsReferences () => IsReferenceOrContainsReferences (); From bc2507827d5c5a7c900bdc61d7f7d3cb3dabf899 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 3 Mar 2020 16:50:59 -0800 Subject: [PATCH 76/83] Style and name. --- .../src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 821ce696cdff68..e054eee0ab2cfa 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -101,7 +101,7 @@ public static void RunModuleConstructor (ModuleHandle module) public static IntPtr AllocateTypeAssociatedMemory (Type type, int size) { - throw new PlatformNotSupported(); + throw new PlatformNotSupportedException (); } [Intrinsic] From d96ef4b2d0c9fb66867074f788a787d96cd896d7 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 5 Mar 2020 20:13:31 -0800 Subject: [PATCH 77/83] Move RuntimeHelpers.AllocateTypeAssociatedMemory tests to src/libraries. --- .../AllocateTypeAssociatedMemory.cs | 43 ------------------- .../CompilerServices/RuntimeHelpersTests.cs | 14 ++++++ 2 files changed, 14 insertions(+), 43 deletions(-) delete mode 100644 src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs deleted file mode 100644 index 860cef4fb3487a..00000000000000 --- a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/AllocateTypeAssociatedMemory.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// -using System; -using TestLibrary; -using System.Runtime.CompilerServices; - -class AllocateTypeAssociatedMemoryTest -{ - private static void ValidateInvalidArguments() - { - try - { - RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); - Assert.Fail("No exception on invalid type"); - } - catch (ArgumentException) - { - } - - try - { - RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), -1); - Assert.Fail("No exception on invalid size"); - } - catch (ArgumentOutOfRangeException) - { - } - } - - private static void ValidateSuccess() - { - IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); - Assert.AreNotEqual(memory, IntPtr.Zero); - } - - public static void Run() - { - ValidateInvalidArguments(); - ValidateSuccess(); - } -} diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index bd4054da4df069..8cf7ca1b8684fd 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -264,6 +264,20 @@ public static void ArrayRangeHelperTest() Assert.Throws(() => { int [] array = RuntimeHelpers.GetSubArray(a, range); }); } + [Fact] + public void AllocateTypeAssociatedMemoryInvalidArguments() + { + Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); }); + Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), -1); }); + } + + [Fact] + public void AllocateTypeAssociatedMemoryValidArguments() + { + IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); + Assert.AreNotEqual(memory, IntPtr.Zero); + } + [StructLayoutAttribute(LayoutKind.Sequential)] private struct StructWithoutReferences { From bfa455dbb5e310c0aee9ad1caaaa3a09f30fddd6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 5 Mar 2020 21:07:32 -0800 Subject: [PATCH 78/83] Missed static --- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 8cf7ca1b8684fd..e9dde99ca3f7a9 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -265,14 +265,14 @@ public static void ArrayRangeHelperTest() } [Fact] - public void AllocateTypeAssociatedMemoryInvalidArguments() + public static void AllocateTypeAssociatedMemoryInvalidArguments() { Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); }); Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), -1); }); } [Fact] - public void AllocateTypeAssociatedMemoryValidArguments() + public static void AllocateTypeAssociatedMemoryValidArguments() { IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); Assert.AreNotEqual(memory, IntPtr.Zero); From b7c5c4496b6ccf4eb60a9838f0e72624be51878b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 6 Mar 2020 13:11:49 -0800 Subject: [PATCH 79/83] Skip tests on Mono. --- .../compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs | 1 - .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs index 4abc5097c8dec0..5b667a78be6d0d 100644 --- a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs +++ b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs @@ -11,7 +11,6 @@ static int Main(string[] args) try { ExecuteCodeWithGuaranteedCleanupTest.Run(); - AllocateTypeAssociatedMemoryTest.Run(); } catch (Exception e) { diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index e9dde99ca3f7a9..71738ff46ac6b2 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -265,6 +265,7 @@ public static void ArrayRangeHelperTest() } [Fact] + [SkipOnMono("Not presently implemented on Mono")] public static void AllocateTypeAssociatedMemoryInvalidArguments() { Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); }); @@ -272,6 +273,7 @@ public static void AllocateTypeAssociatedMemoryInvalidArguments() } [Fact] + [SkipOnMono("Not presently implemented on Mono")] public static void AllocateTypeAssociatedMemoryValidArguments() { IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); From f305cb09e24e5637370d5c6faac70d0386f74392 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 6 Mar 2020 16:52:41 -0800 Subject: [PATCH 80/83] Unit test fix for RuntimeHelpers.AllocateTypeAssociatedMemory(). --- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 71738ff46ac6b2..dea93fa64195b5 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -269,15 +269,15 @@ public static void ArrayRangeHelperTest() public static void AllocateTypeAssociatedMemoryInvalidArguments() { Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); }); - Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), -1); }); + Assert.Throws(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(RuntimeHelpersTests), -1); }); } [Fact] [SkipOnMono("Not presently implemented on Mono")] public static void AllocateTypeAssociatedMemoryValidArguments() { - IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(AllocateTypeAssociatedMemoryTest), 32); - Assert.AreNotEqual(memory, IntPtr.Zero); + IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(RuntimeHelpersTests), 32); + Assert.NotEqual(memory, IntPtr.Zero); } [StructLayoutAttribute(LayoutKind.Sequential)] From 642ca9b390799ea96246982326d978934317844c Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 6 Mar 2020 18:29:40 -0800 Subject: [PATCH 81/83] Bad merge on rebase. --- .../src/System.Private.CoreLib/System.Private.CoreLib.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index d0c8c1b17d145a..7e215d1ad72a8a 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -326,9 +326,6 @@ - - - From fa45add80495450104785eb566869d9ae04228bc Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 6 Mar 2020 19:24:46 -0800 Subject: [PATCH 82/83] Minor rewording for the interoplib interface. --- src/coreclr/src/interop/inc/interoplibimports.h | 2 +- src/coreclr/src/interop/trackerobjectmanager.cpp | 4 ++-- src/coreclr/src/vm/interoplibinterface.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 9d81a2fb625c32..79e753b5c032fa 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -30,7 +30,7 @@ namespace InteropLibImports enum class GcRequest { Default, - Blocking + Extensive // This is an expensive GC request, akin to a Gen2/"stop the world" GC. }; // Request a GC from the runtime. diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index 7581fef58254c9..b7600870354229 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -73,9 +73,9 @@ namespace { InteropLibImports::GcRequest type = InteropLibImports::GcRequest::Default; - // Request an expensive blocking GC when a suspend is occurring. + // Request a "stop the world" GC when a suspend is occurring. if (flags & XAML_REFERENCETRACKER_DISCONNECT_SUSPEND) - type = InteropLibImports::GcRequest::Blocking; + type = InteropLibImports::GcRequest::Extensive; return InteropLibImports::RequestGarbageCollectionForExternal(type); } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 8a399d5a588b58..0b5ec8a3aaf083 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -801,7 +801,7 @@ namespace InteropLibImports BEGIN_EXTERNAL_ENTRYPOINT(&hr) { GCX_COOP_THREAD_EXISTS(GET_THREAD()); - if (req == GcRequest::Blocking) + if (req == GcRequest::Extensive) { GCHeapUtilities::GetGCHeap()->GarbageCollect(2, true, collection_blocking | collection_optimized); } From c7c683995e42908dcbb1498a8a66c1d4d6cc0257 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 6 Mar 2020 21:16:09 -0800 Subject: [PATCH 83/83] Feedback on naming. --- src/coreclr/src/interop/inc/interoplibimports.h | 2 +- src/coreclr/src/interop/trackerobjectmanager.cpp | 2 +- src/coreclr/src/vm/interoplibinterface.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h index 79e753b5c032fa..3217b78a35f339 100644 --- a/src/coreclr/src/interop/inc/interoplibimports.h +++ b/src/coreclr/src/interop/inc/interoplibimports.h @@ -30,7 +30,7 @@ namespace InteropLibImports enum class GcRequest { Default, - Extensive // This is an expensive GC request, akin to a Gen2/"stop the world" GC. + FullBlocking // This is an expensive GC request, akin to a Gen2/"stop the world" GC. }; // Request a GC from the runtime. diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp index b7600870354229..7d1cb15722197a 100644 --- a/src/coreclr/src/interop/trackerobjectmanager.cpp +++ b/src/coreclr/src/interop/trackerobjectmanager.cpp @@ -75,7 +75,7 @@ namespace // Request a "stop the world" GC when a suspend is occurring. if (flags & XAML_REFERENCETRACKER_DISCONNECT_SUSPEND) - type = InteropLibImports::GcRequest::Extensive; + type = InteropLibImports::GcRequest::FullBlocking; return InteropLibImports::RequestGarbageCollectionForExternal(type); } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 0b5ec8a3aaf083..70e5f14f2c7df9 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -801,7 +801,7 @@ namespace InteropLibImports BEGIN_EXTERNAL_ENTRYPOINT(&hr) { GCX_COOP_THREAD_EXISTS(GET_THREAD()); - if (req == GcRequest::Extensive) + if (req == GcRequest::FullBlocking) { GCHeapUtilities::GetGCHeap()->GarbageCollect(2, true, collection_blocking | collection_optimized); }