From 0f7a8697c363d5ac672ba973808e660c6898ef22 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:07:56 +0000 Subject: [PATCH 1/5] Initial plan From ce1fd2ee973209e58f587985303e47c3ace6ec3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:24:00 +0000 Subject: [PATCH 2/5] Fix monotonic CCW vtable and IID publication Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com> --- src/coreclr/vm/comcallablewrapper.cpp | 37 ++++-- src/coreclr/vm/comcallablewrapper.h | 38 ++++-- .../COM/ExtensionPoints/ExtensionPoints.cs | 122 ++++++++++++++++++ 3 files changed, 179 insertions(+), 18 deletions(-) diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 090201cb2ffd71..0163a2c544e3ab 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -3099,16 +3099,15 @@ void ComMethodTable::LayOutClassMethodTable() // IDispatch vtable follows the header CopyMemory(this + 1, pDispVtable, cbTempVtable.Value()); - // Set the layout complete flag and release the lock. - m_Flags |= enum_LayoutComplete; - // We've successfully laid out the class method table so we need to suppress the release of the // memory for the ComCallMethodDescs and store it inside the ComMethodTable so we can // release it when we clean up the ComMethodTable. m_pMDescr = (BYTE*)pMDMemoryPtr; - } + amTracker.SuppressRelease(); - amTracker.SuppressRelease(); + // Publish only after the descriptors and allocation ownership are committed. + SetFlags(enum_LayoutComplete); + } LOG((LF_INTEROP, LL_INFO1000, "LayOutClassMethodTable: %s, parent: %s, this: %p [DONE]\n", m_pMT->GetDebugClassName(), pParentClass ? pParentClass->GetDebugClassName() : 0, this)); } @@ -3290,7 +3289,7 @@ BOOL ComMethodTable::LayOutInterfaceMethodTable(MethodTable* pClsMT) _ASSERTE(numVtableSlots == cbEmittedSlots); // Set the layout complete flag and release the lock. - m_Flags |= enum_LayoutComplete; + SetFlags(enum_LayoutComplete); } #ifdef PROFILING_SUPPORTED @@ -3337,7 +3336,7 @@ void ComMethodTable::LayOutBasicMethodTable() // // Set up the IUnknown and IDispatch methods. Each thread will write exactly the same values to the - // slots so we let it run concurrently and execute a memory barrier by doing InterlockOr at the end. + // slots so we let it run concurrently and publish them with an atomic flag update at the end. // // IDispatch vtable follows the header @@ -3373,11 +3372,33 @@ void ComMethodTable::LayOutBasicMethodTable() // // Set the layout complete flag. // - InterlockedOr((LONG*)&m_Flags, enum_LayoutComplete); + SetFlags(enum_LayoutComplete); LOG((LF_INTEROP, LL_INFO1000, "LayOutClassMethodTable: %s, this: %p [DONE]\n", m_pMT->GetDebugClassName(), this)); } +void ComMethodTable::GenerateIID() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // Metadata generation can throw and trigger GC, so do it outside the commit lock. + IID iid; + GenerateClassItfGuid(TypeHandle(m_pMT), &iid); + + CrstHolder ch(&g_CreateWrapperTemplateCrst); + if (!(VolatileLoad(&m_Flags) & enum_GuidGenerated)) + { + m_IID = iid; + SetFlags(enum_GuidGenerated); + } +} + //-------------------------------------------------------------------------- // Retrieves the DispatchInfo associated with the COM method table. If // the DispatchInfo has not been initialized yet then it is initialized. diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h index 73a53d525f6b05..e81d46082a1a73 100644 --- a/src/coreclr/vm/comcallablewrapper.h +++ b/src/coreclr/vm/comcallablewrapper.h @@ -417,7 +417,7 @@ struct ComMethodTable if (IsIClassXOrBasicItf()) return ifDual; else - return (CorIfaceAttr)(m_Flags & enum_InterfaceTypeMask); + return (CorIfaceAttr)(VolatileLoad(&m_Flags) & enum_InterfaceTypeMask); } CorClassIfaceAttr GetClassInterfaceType() @@ -425,7 +425,7 @@ struct ComMethodTable LIMITED_METHOD_CONTRACT; _ASSERTE(IsIClassXOrBasicItf()); - return (CorClassIfaceAttr)(m_Flags & enum_ClassInterfaceTypeMask); + return (CorClassIfaceAttr)(VolatileLoad(&m_Flags) & enum_ClassInterfaceTypeMask); } BOOL IsIClassX() @@ -437,31 +437,31 @@ struct ComMethodTable BOOL IsIClassXOrBasicItf() { LIMITED_METHOD_CONTRACT; - return (m_Flags & enum_ClassVtableMask) != 0; + return (VolatileLoad(&m_Flags) & enum_ClassVtableMask) != 0; } BOOL IsComClassItf() { LIMITED_METHOD_CONTRACT; - return (m_Flags & enum_ComClassItf) != 0; + return (VolatileLoad(&m_Flags) & enum_ComClassItf) != 0; } BOOL IsLayoutComplete() { LIMITED_METHOD_CONTRACT; - return (m_Flags & enum_LayoutComplete) != 0; + return (VolatileLoad(&m_Flags) & enum_LayoutComplete) != 0; } BOOL IsComVisible() { LIMITED_METHOD_CONTRACT; - return (m_Flags & enum_ComVisible) != 0; + return (VolatileLoad(&m_Flags) & enum_ComVisible) != 0; } BOOL IsBasic() { LIMITED_METHOD_CONTRACT; - return (m_Flags & enum_IsBasic) != 0; + return (VolatileLoad(&m_Flags) & enum_IsBasic) != 0; } BOOL HasInvisibleParent() @@ -639,10 +639,9 @@ struct ComMethodTable CONTRACTL_END; // Generate the IClassX IID if it hasn't been generated yet. - if (!(m_Flags & enum_GuidGenerated)) + if (!(VolatileLoad(&m_Flags) & enum_GuidGenerated)) { - GenerateClassItfGuid(TypeHandle(m_pMT), &m_IID); - m_Flags |= enum_GuidGenerated; + GenerateIID(); } return m_IID; @@ -657,6 +656,25 @@ struct ComMethodTable } private: +#ifndef DACCESS_COMPILE + void GenerateIID(); + + void SetFlags(size_t flags) + { + LIMITED_METHOD_CONTRACT; + + static_assert(offsetof(ComMethodTable, m_Flags) % alignof(size_t) == 0); + + // Preserve other flags while publishing the payload initialized before this update. + size_t oldFlags; + do + { + oldFlags = VolatileLoad(&m_Flags); + } + while (InterlockedCompareExchangeT(&m_Flags, oldFlags | flags, oldFlags) != oldFlags); + } +#endif // DACCESS_COMPILE + SLOT m_ptReserved; //= (SLOT) 0xDEADC0FF; reserved PTR_MethodTable m_pMT; // pointer to the VMs method table ULONG m_cbSlots; // number of slots in the interface (excluding IUnk/IDisp) diff --git a/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs b/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs index 33f27f520d342a..36c6be729af1c4 100644 --- a/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs +++ b/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection; +using System.Reflection.Emit; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using COM; @@ -11,6 +15,124 @@ public class ExtensionPoints { + [Theory] + [InlineData(false)] + [InlineData(true)] + public static unsafe void ConcurrentVtablePublication(bool classLayout) + { + const int MethodCount = 16; + const int WorkerCount = 16; + Guid unsupportedIID = new Guid("9970D486-61A2-4BF9-8395-0C6D8BFA3D2E"); + + for (int iteration = 0; iteration < 8; iteration++) + { + // Fresh types keep both shared vtables cold on every iteration. + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly( + new AssemblyName($"ConcurrentCCW{iteration}"), AssemblyBuilderAccess.Run).DefineDynamicModule("Main"); + var visible = new CustomAttributeBuilder( + typeof(ComVisibleAttribute).GetConstructor(new[] { typeof(bool) }), new object[] { true }); + TypeBuilder interfaceBuilder = module.DefineType("ITest", + TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); + interfaceBuilder.SetCustomAttribute(visible); + interfaceBuilder.SetCustomAttribute(new CustomAttributeBuilder( + typeof(InterfaceTypeAttribute).GetConstructor(new[] { typeof(ComInterfaceType) }), + new object[] { ComInterfaceType.InterfaceIsIUnknown })); + for (int slot = 0; slot < MethodCount; slot++) + { + interfaceBuilder.DefineMethod($"Method{slot}", + MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, + typeof(int), Type.EmptyTypes); + } + Type interfaceType = interfaceBuilder.CreateType(); + + TypeBuilder classBuilder = module.DefineType("Test", TypeAttributes.Public); + classBuilder.SetCustomAttribute(visible); + classBuilder.SetCustomAttribute(new CustomAttributeBuilder( + typeof(ClassInterfaceAttribute).GetConstructor(new[] { typeof(ClassInterfaceType) }), + new object[] { ClassInterfaceType.AutoDual })); + classBuilder.AddInterfaceImplementation(interfaceType); + for (int slot = 0; slot < MethodCount; slot++) + { + MethodBuilder method = classBuilder.DefineMethod($"Method{slot}", + MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), Type.EmptyTypes); + ILGenerator il = method.GetILGenerator(); + il.Emit(OpCodes.Ldc_I4, slot); + il.Emit(OpCodes.Ret); + } + Type classType = classBuilder.CreateType(); + + var instances = new object[WorkerCount]; + var interfaces = new nint[WorkerCount]; + var workers = new Task[WorkerCount]; + using var start = new Barrier(WorkerCount); + try + { + for (int worker = 0; worker < WorkerCount; worker++) + { + instances[worker] = Activator.CreateInstance(classType); + if (classLayout) + { + // An explicit interface does not lay out the class interface. + interfaces[worker] = Marshal.GetComInterfaceForObject(instances[worker], interfaceType); + } + } + + for (int worker = 0; worker < WorkerCount; worker++) + { + int index = worker; + workers[worker] = Task.Factory.StartNew(() => + { + nint pointer = 0; + try + { + start.SignalAndWait(); + Guid iid = unsupportedIID; + if (classLayout && (index & 1) != 0) + { + // Failed class-IID lookup generates the lazy IID without doing class layout. + Assert.Equal(unchecked((int)0x80004002), + Marshal.QueryInterface(interfaces[index], in iid, out nint result)); + Assert.Equal((nint)0, result); + } + pointer = classLayout + ? Marshal.GetIUnknownForObject(instances[index]) + : Marshal.GetComInterfaceForObject(instances[index], interfaceType); + + iid = new Guid("00000000-0000-0000-C000-000000000046"); + Assert.Equal(0, Marshal.QueryInterface(pointer, in iid, out nint unknown)); + Marshal.Release(unknown); + + nint methodInterface = classLayout ? interfaces[index] : pointer; + nint* vtable = *(nint**)methodInterface; + for (int slot = 0; slot < MethodCount; slot++) + { + int value = -1; + var invoke = (delegate* unmanaged[Stdcall])vtable[3 + slot]; + Assert.Equal(0, invoke(methodInterface, &value)); + Assert.Equal(slot, value); + } + } + finally + { + if (pointer != 0) + Marshal.Release(pointer); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + Task.WaitAll(workers); + } + finally + { + foreach (nint pointer in interfaces) + { + if (pointer != 0) + Marshal.Release(pointer); + } + GC.KeepAlive(instances); + } + } + } + unsafe class MallocSpy : IMallocSpy { private int _called = 0; From 77de2f7eec21ab6df9bc794dacced46d26ccf482 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Oct 2026 17:07:40 +0000 Subject: [PATCH 3/5] Use full-width interlocked OR for CCW flags Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com> --- src/coreclr/vm/comcallablewrapper.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h index e81d46082a1a73..2e770ed1ef01d5 100644 --- a/src/coreclr/vm/comcallablewrapper.h +++ b/src/coreclr/vm/comcallablewrapper.h @@ -666,12 +666,11 @@ struct ComMethodTable static_assert(offsetof(ComMethodTable, m_Flags) % alignof(size_t) == 0); // Preserve other flags while publishing the payload initialized before this update. - size_t oldFlags; - do - { - oldFlags = VolatileLoad(&m_Flags); - } - while (InterlockedCompareExchangeT(&m_Flags, oldFlags | flags, oldFlags) != oldFlags); +#ifdef HOST_64BIT + InterlockedOr64(reinterpret_cast(&m_Flags), static_cast(flags)); +#else + InterlockedOr(reinterpret_cast(&m_Flags), static_cast(flags)); +#endif // HOST_64BIT } #endif // DACCESS_COMPILE From d19447bb1be85a526d7346231fbba62498f3369d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:57:00 +0000 Subject: [PATCH 4/5] Remove non-reproducing concurrent CCW stress test Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com> --- .../COM/ExtensionPoints/ExtensionPoints.cs | 122 ------------------ 1 file changed, 122 deletions(-) diff --git a/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs b/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs index 36c6be729af1c4..33f27f520d342a 100644 --- a/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs +++ b/src/tests/Interop/COM/ExtensionPoints/ExtensionPoints.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Reflection; -using System.Reflection.Emit; using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; using COM; @@ -15,124 +11,6 @@ public class ExtensionPoints { - [Theory] - [InlineData(false)] - [InlineData(true)] - public static unsafe void ConcurrentVtablePublication(bool classLayout) - { - const int MethodCount = 16; - const int WorkerCount = 16; - Guid unsupportedIID = new Guid("9970D486-61A2-4BF9-8395-0C6D8BFA3D2E"); - - for (int iteration = 0; iteration < 8; iteration++) - { - // Fresh types keep both shared vtables cold on every iteration. - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly( - new AssemblyName($"ConcurrentCCW{iteration}"), AssemblyBuilderAccess.Run).DefineDynamicModule("Main"); - var visible = new CustomAttributeBuilder( - typeof(ComVisibleAttribute).GetConstructor(new[] { typeof(bool) }), new object[] { true }); - TypeBuilder interfaceBuilder = module.DefineType("ITest", - TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); - interfaceBuilder.SetCustomAttribute(visible); - interfaceBuilder.SetCustomAttribute(new CustomAttributeBuilder( - typeof(InterfaceTypeAttribute).GetConstructor(new[] { typeof(ComInterfaceType) }), - new object[] { ComInterfaceType.InterfaceIsIUnknown })); - for (int slot = 0; slot < MethodCount; slot++) - { - interfaceBuilder.DefineMethod($"Method{slot}", - MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, - typeof(int), Type.EmptyTypes); - } - Type interfaceType = interfaceBuilder.CreateType(); - - TypeBuilder classBuilder = module.DefineType("Test", TypeAttributes.Public); - classBuilder.SetCustomAttribute(visible); - classBuilder.SetCustomAttribute(new CustomAttributeBuilder( - typeof(ClassInterfaceAttribute).GetConstructor(new[] { typeof(ClassInterfaceType) }), - new object[] { ClassInterfaceType.AutoDual })); - classBuilder.AddInterfaceImplementation(interfaceType); - for (int slot = 0; slot < MethodCount; slot++) - { - MethodBuilder method = classBuilder.DefineMethod($"Method{slot}", - MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), Type.EmptyTypes); - ILGenerator il = method.GetILGenerator(); - il.Emit(OpCodes.Ldc_I4, slot); - il.Emit(OpCodes.Ret); - } - Type classType = classBuilder.CreateType(); - - var instances = new object[WorkerCount]; - var interfaces = new nint[WorkerCount]; - var workers = new Task[WorkerCount]; - using var start = new Barrier(WorkerCount); - try - { - for (int worker = 0; worker < WorkerCount; worker++) - { - instances[worker] = Activator.CreateInstance(classType); - if (classLayout) - { - // An explicit interface does not lay out the class interface. - interfaces[worker] = Marshal.GetComInterfaceForObject(instances[worker], interfaceType); - } - } - - for (int worker = 0; worker < WorkerCount; worker++) - { - int index = worker; - workers[worker] = Task.Factory.StartNew(() => - { - nint pointer = 0; - try - { - start.SignalAndWait(); - Guid iid = unsupportedIID; - if (classLayout && (index & 1) != 0) - { - // Failed class-IID lookup generates the lazy IID without doing class layout. - Assert.Equal(unchecked((int)0x80004002), - Marshal.QueryInterface(interfaces[index], in iid, out nint result)); - Assert.Equal((nint)0, result); - } - pointer = classLayout - ? Marshal.GetIUnknownForObject(instances[index]) - : Marshal.GetComInterfaceForObject(instances[index], interfaceType); - - iid = new Guid("00000000-0000-0000-C000-000000000046"); - Assert.Equal(0, Marshal.QueryInterface(pointer, in iid, out nint unknown)); - Marshal.Release(unknown); - - nint methodInterface = classLayout ? interfaces[index] : pointer; - nint* vtable = *(nint**)methodInterface; - for (int slot = 0; slot < MethodCount; slot++) - { - int value = -1; - var invoke = (delegate* unmanaged[Stdcall])vtable[3 + slot]; - Assert.Equal(0, invoke(methodInterface, &value)); - Assert.Equal(slot, value); - } - } - finally - { - if (pointer != 0) - Marshal.Release(pointer); - } - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - Task.WaitAll(workers); - } - finally - { - foreach (nint pointer in interfaces) - { - if (pointer != 0) - Marshal.Release(pointer); - } - GC.KeepAlive(instances); - } - } - } - unsafe class MallocSpy : IMallocSpy { private int _called = 0; From fa37c5aec2290af394357a0f4a5937713940f5c2 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 15 Oct 2026 09:30:14 -0700 Subject: [PATCH 5/5] Simplify CCW flags and descriptor bookkeeping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d60c7d11-aac7-47b6-8330-fa7d12e79437 --- src/coreclr/vm/comcallablewrapper.cpp | 50 +++------------------------ src/coreclr/vm/comcallablewrapper.h | 47 ++++--------------------- 2 files changed, 12 insertions(+), 85 deletions(-) diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 0163a2c544e3ab..d94abb968049b3 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -2723,7 +2723,7 @@ LONG ComCallWrapperCache::Release() //-------------------------------------------------------------------------- // void ComMethodTable::Cleanup() -// free the stubs and the vtable +// Release the cached dispatch and type information. //-------------------------------------------------------------------------- void ComMethodTable::Cleanup() { @@ -2735,37 +2735,12 @@ void ComMethodTable::Cleanup() } CONTRACTL_END; - unsigned cbExtraSlots = GetNumExtraSlots(GetInterfaceType()); - unsigned cbSlots = m_cbSlots; - - SLOT* pComVtable = (SLOT *)(this + 1); - - // If we have created and laid out the method desc then we need to delete them. - if (IsLayoutComplete()) - { -#ifdef PROFILING_SUPPORTED - // We used to issue the COMClassicVTableDestroyed callback from here. - // However, that causes an AV. At this point the MethodTable is gone - // (as the AppDomain containing it has been unloaded), but the ComMethodTable - // still points to it. The code here used to wrap a TypeHandle around the - // MethodTable pointer, cast to a ClassID, and then call COMClassicVTableDestroyed. - // But the act of casting to a TypeHandle invokes debug-code to verify the - // MethodTable, which causes an AV. - // - // For now, we're not issuing the COMClassicVTableDestroyed callback anymore. - // Reexamine the profiling API around - // CCWs and move the callback elsewhere and / or rethink the current - // set of CCW callbacks to mirror reality more accurately. -#endif // PROFILING_SUPPORTED - } - if (m_pDispatchInfo) delete m_pDispatchInfo; if (m_pITypeInfo && !IsAtProcessExit()) SafeRelease(m_pITypeInfo); - // The m_pMDescr and the current instance is allocated from the related LoaderAllocator - // so no cleanup is needed here. + // The method descriptors, entry thunks, and this instance belong to the LoaderAllocator. } namespace @@ -2813,8 +2788,6 @@ void ComMethodTable::LayOutClassMethodTable() IDispatchVtable* pDispVtable; SLOT *pComVtable; unsigned cbPrevSlots = 0; - unsigned cbAlloc = 0; - BYTE* pMDMemoryPtr = nullptr; AllocMemTracker amTracker; LoaderAllocator* pLoaderAllocator = m_pMT->GetLoaderAllocator(); ComCallMethodDesc* pMethodDescMemory = NULL; @@ -2914,18 +2887,12 @@ void ComMethodTable::LayOutClassMethodTable() // Allocate method desc's for the rest of the slots. // unsigned cbMethodDescs = sizeof(ComCallMethodDesc) * (m_cbSlots - cbParentComMTSlots); - cbAlloc = cbMethodDescs; - if (cbAlloc > 0) + if (cbMethodDescs > 0) { - pMDMemoryPtr = (BYTE*)amTracker.Track(m_pMT->GetLoaderAllocator()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbAlloc + sizeof(UINT_PTR)))); + pMethodDescMemory = (ComCallMethodDesc*)amTracker.Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbMethodDescs))); // initialize the method desc memory to zero - FillMemory(pMDMemoryPtr, cbAlloc + sizeof(UINT_PTR), 0x0); - - *(UINT_PTR *)(pMDMemoryPtr) = cbMethodDescs; // fill in the size of the method desc's - - // move past the size - pMethodDescMemory = (ComCallMethodDesc*)(pMDMemoryPtr + sizeof(UINT_PTR)); + FillMemory(pMethodDescMemory, cbMethodDescs, 0x0); } _ASSERTE(0 == (((DWORD_PTR)pMethodDescMemory) & (sizeof(void*)-1))); @@ -3099,10 +3066,6 @@ void ComMethodTable::LayOutClassMethodTable() // IDispatch vtable follows the header CopyMemory(this + 1, pDispVtable, cbTempVtable.Value()); - // We've successfully laid out the class method table so we need to suppress the release of the - // memory for the ComCallMethodDescs and store it inside the ComMethodTable so we can - // release it when we clean up the ComMethodTable. - m_pMDescr = (BYTE*)pMDMemoryPtr; amTracker.SuppressRelease(); // Publish only after the descriptors and allocation ownership are committed. @@ -3985,7 +3948,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForClass(MethodTable pComMT->m_ptReserved = (SLOT)(size_t)0xDEADC0FF; // reserved pComMT->m_pMT = pClassMT; // pointer to the class method table pComMT->m_cbRefCount = 0; - pComMT->m_pMDescr = NULL; pComMT->m_pITypeInfo = NULL; pComMT->m_pDispatchInfo = NULL; pComMT->m_cbSlots = cbTotalSlots.Value(); // number of slots not counting IDisp methods. @@ -4059,7 +4021,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForInterface(MethodT pComMT->m_pMT = pInterfaceMT; // pointer to the interface's method table pComMT->m_cbSlots = cbComSlots; // number of slots not counting IUnk pComMT->m_cbRefCount = 0; - pComMT->m_pMDescr = NULL; pComMT->m_pITypeInfo = NULL; pComMT->m_pDispatchInfo = NULL; @@ -4118,7 +4079,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForBasic(MethodTable pComMT->m_pMT = pMT; pComMT->m_cbSlots = 0; // number of slots not counting IUnk pComMT->m_cbRefCount = 0; - pComMT->m_pMDescr = NULL; pComMT->m_pITypeInfo = NULL; pComMT->m_pDispatchInfo = NULL; diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h index 2e770ed1ef01d5..7ac864d058c0b3 100644 --- a/src/coreclr/vm/comcallablewrapper.h +++ b/src/coreclr/vm/comcallablewrapper.h @@ -417,7 +417,7 @@ struct ComMethodTable if (IsIClassXOrBasicItf()) return ifDual; else - return (CorIfaceAttr)(VolatileLoad(&m_Flags) & enum_InterfaceTypeMask); + return (CorIfaceAttr)(m_Flags & enum_InterfaceTypeMask); } CorClassIfaceAttr GetClassInterfaceType() @@ -425,7 +425,7 @@ struct ComMethodTable LIMITED_METHOD_CONTRACT; _ASSERTE(IsIClassXOrBasicItf()); - return (CorClassIfaceAttr)(VolatileLoad(&m_Flags) & enum_ClassInterfaceTypeMask); + return (CorClassIfaceAttr)(m_Flags & enum_ClassInterfaceTypeMask); } BOOL IsIClassX() @@ -437,13 +437,13 @@ struct ComMethodTable BOOL IsIClassXOrBasicItf() { LIMITED_METHOD_CONTRACT; - return (VolatileLoad(&m_Flags) & enum_ClassVtableMask) != 0; + return (m_Flags & enum_ClassVtableMask) != 0; } BOOL IsComClassItf() { LIMITED_METHOD_CONTRACT; - return (VolatileLoad(&m_Flags) & enum_ComClassItf) != 0; + return (m_Flags & enum_ComClassItf) != 0; } BOOL IsLayoutComplete() @@ -455,13 +455,13 @@ struct ComMethodTable BOOL IsComVisible() { LIMITED_METHOD_CONTRACT; - return (VolatileLoad(&m_Flags) & enum_ComVisible) != 0; + return (m_Flags & enum_ComVisible) != 0; } BOOL IsBasic() { LIMITED_METHOD_CONTRACT; - return (VolatileLoad(&m_Flags) & enum_IsBasic) != 0; + return (m_Flags & enum_IsBasic) != 0; } BOOL HasInvisibleParent() @@ -558,38 +558,6 @@ struct ComMethodTable return (ComCallMethodDesc *)pCMD; } - BOOL OwnedbyThisMT(unsigned slotIndex) - { - CONTRACTL - { - WRAPPER(THROWS); - WRAPPER(GC_TRIGGERS); - MODE_ANY; - } - CONTRACTL_END; - - if (!IsIClassXOrBasicItf()) - return TRUE; - - if (m_pMDescr != NULL) - { - // These are the methods from the default interfaces such as IUnknown. - unsigned cbExtraSlots = GetNumExtraSlots(GetInterfaceType()); - - // Refer to ComMethodTable::LayOutClassMethodTable(). - ULONG cbSize = *(ULONG *)m_pMDescr; - ULONG cbNewSlots = cbSize / sizeof(ComCallMethodDesc); - _ASSERTE( (cbSize % sizeof(ComCallMethodDesc)) == 0); - - // m_cbSlots is the total number of methods in addition to the ones from the - // default interfaces. cbNewSlots is the total number of methods introduced - // by this class (== m_cbSlots - ). - return slotIndex >= (cbExtraSlots + m_cbSlots - cbNewSlots); - } - - return FALSE; - } - ComMethodTable *GetParentClassComMT(); static inline PTR_ComMethodTable ComMethodTableFromIP(PTR_IUnknown pUnk) @@ -679,7 +647,6 @@ struct ComMethodTable ULONG m_cbSlots; // number of slots in the interface (excluding IUnk/IDisp) LONG m_cbRefCount; // ref-count the vtable as it is being shared size_t m_Flags; // make sure this is initialized to zero - LPVOID m_pMDescr; // pointer to methoddescr.s owned by this MT ITypeInfo* m_pITypeInfo; // cached pointer to ITypeInfo DispatchInfo* m_pDispatchInfo; // The dispatch info used to expose IDispatch to COM. IID m_IID; // The IID of the interface. @@ -688,7 +655,7 @@ struct ComMethodTable // SLOT m_slots[]; // vtable entries (m_cbSlots of them, plus the 3 or 7 from IUnk/IDisp) // For interface COM method tables, an inline ComCallMethodDesc m_comCallMethodDesc[] array (m_cbSlots entries) // may follow the slots. For class-interface layouts, the ComCallMethodDesc[] block is allocated separately and - // referenced via m_pMDescr. Basic COM method tables may have no ComCallMethodDesc descriptors at all. + // referenced by the entry thunks. Basic COM method tables may have no ComCallMethodDesc descriptors at all. friend struct ::cdac_data; };