diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp
index 090201cb2ffd71..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,17 +3066,12 @@ 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;
+ amTracker.SuppressRelease();
- // 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;
+ // Publish only after the descriptors and allocation ownership are committed.
+ SetFlags(enum_LayoutComplete);
}
- amTracker.SuppressRelease();
-
LOG((LF_INTEROP, LL_INFO1000, "LayOutClassMethodTable: %s, parent: %s, this: %p [DONE]\n", m_pMT->GetDebugClassName(), pParentClass ? pParentClass->GetDebugClassName() : 0, this));
}
@@ -3290,7 +3252,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 +3299,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 +3335,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.
@@ -3964,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.
@@ -4038,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;
@@ -4097,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 73a53d525f6b05..7ac864d058c0b3 100644
--- a/src/coreclr/vm/comcallablewrapper.h
+++ b/src/coreclr/vm/comcallablewrapper.h
@@ -449,7 +449,7 @@ struct ComMethodTable
BOOL IsLayoutComplete()
{
LIMITED_METHOD_CONTRACT;
- return (m_Flags & enum_LayoutComplete) != 0;
+ return (VolatileLoad(&m_Flags) & enum_LayoutComplete) != 0;
}
BOOL IsComVisible()
@@ -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)
@@ -639,10 +607,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,12 +624,29 @@ 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.
+#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
+
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)
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.
@@ -671,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;
};