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; };