From 4c461d754ff29d1ba37c145676c397062378d1c0 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sat, 1 Dec 2018 10:31:35 +0100 Subject: [PATCH] Enable COM interop for collectible classes (#20919) * Enable COM interop for collectible classes * Modify DispatchInfo to use LoaderAllocator handles The DispatchMemberInfo was using global handles to refer to the managed MemberInfo instances. That doesn't work with unloadability. This change modifies it to use handles allocated from LoaderAllocator. * Disable COM interop for WinRT types * Remove collectible check from IsTypeVisibleFromCom. That fixes three new COM interop tests * Add collectible check to GetComClassFactory when we check for unsupported interop with WinRT * Add COM unloadability tests Add two tests to test COM unloadability: * One for using native COM server from managed COM client * One for using managed COM objects from native client * Add unloading test for IUnknownTest * Disable NETClientPrimitivesInALC on Win ARM The NETClientPrimitives is disabled there too. --- src/System.Private.CoreLib/Resources/Strings.resx | 4 +- src/inc/CrstTypes.def | 3 + src/inc/crsttypes.h | 267 +++++++++++---------- src/vm/appdomain.cpp | 45 ---- src/vm/appdomain.hpp | 50 ---- src/vm/ceeload.cpp | 6 + src/vm/comcallablewrapper.cpp | 73 ++---- src/vm/comcallablewrapper.h | 49 +--- src/vm/comtoclrcall.h | 2 +- src/vm/dispatchinfo.cpp | 84 +++---- src/vm/dispatchinfo.h | 17 +- src/vm/interoputil.cpp | 10 +- src/vm/loaderallocator.cpp | 109 +++++++++ src/vm/loaderallocator.hpp | 41 ++++ src/vm/methodtable.cpp | 6 +- src/vm/runtimecallablewrapper.cpp | 18 -- src/vm/stdinterfaces.cpp | 4 +- tests/issues.targets | 3 + tests/src/Common/CoreCLRTestLibrary/Utilities.cs | 60 +++++ .../Primitives/NETClientPrimitivesInALC.csproj | 38 +++ .../Interop/COM/NETClients/Primitives/TestInALC.cs | 19 ++ .../MarshalAPI/IUnknown/IUnknownTest.csproj | 2 +- .../MarshalAPI/IUnknown/IUnknownTestInALC.csproj | 45 ++++ tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs | 19 ++ .../Default/DefaultTestInALC.csproj | 38 +++ .../Default/TestInALC.cs | 19 ++ 26 files changed, 623 insertions(+), 408 deletions(-) create mode 100644 tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj create mode 100644 tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs create mode 100644 tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj create mode 100644 tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs create mode 100644 tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj create mode 100644 tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index 9fa2e10..e5de28b 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -2884,8 +2884,8 @@ A non-collectible assembly may not reference a collectible assembly. - - COM Interop is not supported for collectible types. + + WinRT Interop is not supported for collectible types. CreateInstance cannot be used with an object of type TypeBuilder. diff --git a/src/inc/CrstTypes.def b/src/inc/CrstTypes.def index 180171e..bb996a7 100644 --- a/src/inc/CrstTypes.def +++ b/src/inc/CrstTypes.def @@ -696,3 +696,6 @@ End Crst TieredCompilation AcquiredBefore ThreadpoolTimerQueue End + +Crst COMCallWrapper +End diff --git a/src/inc/crsttypes.h b/src/inc/crsttypes.h index 2335843..7c867f7 100644 --- a/src/inc/crsttypes.h +++ b/src/inc/crsttypes.h @@ -35,138 +35,139 @@ enum CrstType CrstCLRPrivBinderMaps = 16, CrstCLRPrivBinderMapsAdd = 17, CrstCodeFragmentHeap = 18, - CrstCOMWrapperCache = 19, - CrstConnectionNameTable = 20, - CrstContexts = 21, - CrstCoreCLRBinderLog = 22, - CrstCrstCLRPrivBinderLocalWinMDPath = 23, - CrstCSPCache = 24, - CrstDataTest1 = 25, - CrstDataTest2 = 26, - CrstDbgTransport = 27, - CrstDeadlockDetection = 28, - CrstDebuggerController = 29, - CrstDebuggerFavorLock = 30, - CrstDebuggerHeapExecMemLock = 31, - CrstDebuggerHeapLock = 32, - CrstDebuggerJitInfo = 33, - CrstDebuggerMutex = 34, - CrstDelegateToFPtrHash = 35, - CrstDomainLocalBlock = 36, - CrstDynamicIL = 37, - CrstDynamicMT = 38, - CrstDynLinkZapItems = 39, - CrstEtwTypeLogHash = 40, - CrstEventPipe = 41, - CrstEventStore = 42, - CrstException = 43, - CrstExecuteManLock = 44, - CrstExecuteManRangeLock = 45, - CrstFCall = 46, - CrstFriendAccessCache = 47, - CrstFuncPtrStubs = 48, - CrstFusionAppCtx = 49, - CrstGCCover = 50, - CrstGCMemoryPressure = 51, - CrstGlobalStrLiteralMap = 52, - CrstHandleTable = 53, - CrstHostAssemblyMap = 54, - CrstHostAssemblyMapAdd = 55, - CrstIbcProfile = 56, - CrstIJWFixupData = 57, - CrstIJWHash = 58, - CrstILStubGen = 59, - CrstInlineTrackingMap = 60, - CrstInstMethodHashTable = 61, - CrstInterfaceVTableMap = 62, - CrstInterop = 63, - CrstInteropData = 64, - CrstIOThreadpoolWorker = 65, - CrstIsJMCMethod = 66, - CrstISymUnmanagedReader = 67, - CrstJit = 68, - CrstJitGenericHandleCache = 69, - CrstJitPerf = 70, - CrstJumpStubCache = 71, - CrstLeafLock = 72, - CrstListLock = 73, - CrstLoaderAllocator = 74, - CrstLoaderAllocatorReferences = 75, - CrstLoaderHeap = 76, - CrstMda = 77, - CrstMetadataTracker = 78, - CrstModIntPairList = 79, - CrstModule = 80, - CrstModuleFixup = 81, - CrstModuleLookupTable = 82, - CrstMulticoreJitHash = 83, - CrstMulticoreJitManager = 84, - CrstMUThunkHash = 85, - CrstNativeBinderInit = 86, - CrstNativeImageCache = 87, - CrstNls = 88, - CrstNotifyGdb = 89, - CrstObjectList = 90, - CrstOnEventManager = 91, - CrstPatchEntryPoint = 92, - CrstPEImage = 93, - CrstPEImagePDBStream = 94, - CrstPendingTypeLoadEntry = 95, - CrstPinHandle = 96, - CrstPinnedByrefValidation = 97, - CrstProfilerGCRefDataFreeList = 98, - CrstProfilingAPIStatus = 99, - CrstPublisherCertificate = 100, - CrstRCWCache = 101, - CrstRCWCleanupList = 102, - CrstRCWRefCache = 103, - CrstReadyToRunEntryPointToMethodDescMap = 104, - CrstReDacl = 105, - CrstReflection = 106, - CrstReJITDomainTable = 107, - CrstReJITGlobalRequest = 108, - CrstRemoting = 109, - CrstRetThunkCache = 110, - CrstRWLock = 111, - CrstSavedExceptionInfo = 112, - CrstSaveModuleProfileData = 113, - CrstSecurityStackwalkCache = 114, - CrstSharedAssemblyCreate = 115, - CrstSigConvert = 116, - CrstSingleUseLock = 117, - CrstSpecialStatics = 118, - CrstSqmManager = 119, - CrstStackSampler = 120, - CrstStressLog = 121, - CrstStrongName = 122, - CrstStubCache = 123, - CrstStubDispatchCache = 124, - CrstStubUnwindInfoHeapSegments = 125, - CrstSyncBlockCache = 126, - CrstSyncHashLock = 127, - CrstSystemBaseDomain = 128, - CrstSystemDomain = 129, - CrstSystemDomainDelayedUnloadList = 130, - CrstThreadIdDispenser = 131, - CrstThreadpoolEventCache = 132, - CrstThreadpoolTimerQueue = 133, - CrstThreadpoolWaitThreads = 134, - CrstThreadpoolWorker = 135, - CrstThreadStaticDataHashTable = 136, - CrstThreadStore = 137, - CrstTieredCompilation = 138, - CrstTPMethodTable = 139, - CrstTypeEquivalenceMap = 140, - CrstTypeIDMap = 141, - CrstUMEntryThunkCache = 142, - CrstUMThunkHash = 143, - CrstUniqueStack = 144, - CrstUnresolvedClassLock = 145, - CrstUnwindInfoTableLock = 146, - CrstVSDIndirectionCellLock = 147, - CrstWinRTFactoryCache = 148, - CrstWrapperTemplate = 149, - kNumberOfCrstTypes = 150 + CrstCOMCallWrapper = 19, + CrstCOMWrapperCache = 20, + CrstConnectionNameTable = 21, + CrstContexts = 22, + CrstCoreCLRBinderLog = 23, + CrstCrstCLRPrivBinderLocalWinMDPath = 24, + CrstCSPCache = 25, + CrstDataTest1 = 26, + CrstDataTest2 = 27, + CrstDbgTransport = 28, + CrstDeadlockDetection = 29, + CrstDebuggerController = 30, + CrstDebuggerFavorLock = 31, + CrstDebuggerHeapExecMemLock = 32, + CrstDebuggerHeapLock = 33, + CrstDebuggerJitInfo = 34, + CrstDebuggerMutex = 35, + CrstDelegateToFPtrHash = 36, + CrstDomainLocalBlock = 37, + CrstDynamicIL = 38, + CrstDynamicMT = 39, + CrstDynLinkZapItems = 40, + CrstEtwTypeLogHash = 41, + CrstEventPipe = 42, + CrstEventStore = 43, + 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, + CrstJitPerf = 71, + CrstJumpStubCache = 72, + CrstLeafLock = 73, + CrstListLock = 74, + CrstLoaderAllocator = 75, + CrstLoaderAllocatorReferences = 76, + CrstLoaderHeap = 77, + CrstMda = 78, + CrstMetadataTracker = 79, + CrstModIntPairList = 80, + CrstModule = 81, + CrstModuleFixup = 82, + CrstModuleLookupTable = 83, + CrstMulticoreJitHash = 84, + CrstMulticoreJitManager = 85, + CrstMUThunkHash = 86, + CrstNativeBinderInit = 87, + CrstNativeImageCache = 88, + CrstNls = 89, + CrstNotifyGdb = 90, + CrstObjectList = 91, + CrstOnEventManager = 92, + CrstPatchEntryPoint = 93, + CrstPEImage = 94, + CrstPEImagePDBStream = 95, + CrstPendingTypeLoadEntry = 96, + CrstPinHandle = 97, + CrstPinnedByrefValidation = 98, + CrstProfilerGCRefDataFreeList = 99, + CrstProfilingAPIStatus = 100, + CrstPublisherCertificate = 101, + CrstRCWCache = 102, + CrstRCWCleanupList = 103, + CrstRCWRefCache = 104, + CrstReadyToRunEntryPointToMethodDescMap = 105, + CrstReDacl = 106, + CrstReflection = 107, + CrstReJITDomainTable = 108, + CrstReJITGlobalRequest = 109, + CrstRemoting = 110, + CrstRetThunkCache = 111, + CrstRWLock = 112, + CrstSavedExceptionInfo = 113, + CrstSaveModuleProfileData = 114, + CrstSecurityStackwalkCache = 115, + CrstSharedAssemblyCreate = 116, + CrstSigConvert = 117, + CrstSingleUseLock = 118, + CrstSpecialStatics = 119, + CrstSqmManager = 120, + CrstStackSampler = 121, + CrstStressLog = 122, + CrstStrongName = 123, + CrstStubCache = 124, + CrstStubDispatchCache = 125, + CrstStubUnwindInfoHeapSegments = 126, + CrstSyncBlockCache = 127, + CrstSyncHashLock = 128, + CrstSystemBaseDomain = 129, + CrstSystemDomain = 130, + CrstSystemDomainDelayedUnloadList = 131, + CrstThreadIdDispenser = 132, + CrstThreadpoolEventCache = 133, + CrstThreadpoolTimerQueue = 134, + CrstThreadpoolWaitThreads = 135, + CrstThreadpoolWorker = 136, + CrstThreadStaticDataHashTable = 137, + CrstThreadStore = 138, + CrstTieredCompilation = 139, + CrstTPMethodTable = 140, + CrstTypeEquivalenceMap = 141, + CrstTypeIDMap = 142, + CrstUMEntryThunkCache = 143, + CrstUMThunkHash = 144, + CrstUniqueStack = 145, + CrstUnresolvedClassLock = 146, + CrstUnwindInfoTableLock = 147, + CrstVSDIndirectionCellLock = 148, + CrstWinRTFactoryCache = 149, + CrstWrapperTemplate = 150, + kNumberOfCrstTypes = 151 }; #endif // __CRST_TYPES_INCLUDED @@ -196,6 +197,7 @@ int g_rgCrstLevelMap[] = 0, // CrstCLRPrivBinderMaps 3, // CrstCLRPrivBinderMapsAdd 6, // CrstCodeFragmentHeap + 0, // CrstCOMCallWrapper 4, // CrstCOMWrapperCache 0, // CrstConnectionNameTable 15, // CrstContexts @@ -351,6 +353,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstCLRPrivBinderMaps", "CrstCLRPrivBinderMapsAdd", "CrstCodeFragmentHeap", + "CrstCOMCallWrapper", "CrstCOMWrapperCache", "CrstConnectionNameTable", "CrstContexts", diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 19999cd..1f97825 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -3554,7 +3554,6 @@ AppDomain::AppDomain() m_dwFlags = 0; m_pDefaultContext = NULL; #ifdef FEATURE_COMINTEROP - m_pComCallWrapperCache = NULL; m_pRCWCache = NULL; m_pRCWRefCache = NULL; m_pLicenseInteropHelperMT = NULL; @@ -3892,28 +3891,6 @@ void AppDomain::Terminate() delete m_pRCWRefCache; m_pRCWRefCache = NULL; } - - if (m_pComCallWrapperCache) - { - m_pComCallWrapperCache->Neuter(); - m_pComCallWrapperCache->Release(); - } - - // if the above released the wrapper cache, then it will call back and reset our - // m_pComCallWrapperCache to null. If not null, then need to set it's domain pointer to - // null. - if (! m_pComCallWrapperCache) - { - LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache released\n")); - } -#ifdef _DEBUG - else - { - m_pComCallWrapperCache = NULL; - LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache not released\n")); - } -#endif // _DEBUG - #endif // FEATURE_COMINTEROP @@ -6910,28 +6887,6 @@ BOOL AppDomain::WasSystemAssemblyLoadEventSent(void) #ifdef FEATURE_COMINTEROP -ComCallWrapperCache *AppDomain::GetComCallWrapperCache() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - if (! m_pComCallWrapperCache) - { - BaseDomain::LockHolder lh(this); - - if (! m_pComCallWrapperCache) - m_pComCallWrapperCache = ComCallWrapperCache::Create(this); - } - _ASSERTE(m_pComCallWrapperCache); - return m_pComCallWrapperCache; -} - RCWRefCache *AppDomain::GetRCWRefCache() { CONTRACT(RCWRefCache*) diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index b326b26..4163c30 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -1101,45 +1101,6 @@ public: // This will look up interop data for a method table // -#ifndef DACCESS_COMPILE - // Returns the data pointer if present, NULL otherwise - InteropMethodTableData *LookupComInteropData(MethodTable *pMT) - { - // Take the lock - CrstHolder holder(&m_InteropDataCrst); - - // Lookup - InteropMethodTableData *pData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); - - // Not there... - if (pData == (InteropMethodTableData*) INVALIDENTRY) - return NULL; - - // Found it - return pData; - } - - // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry - BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData) - { - // We don't keep track of this kind of information for interfaces - _ASSERTE(!pMT->IsInterface()); - - // Take the lock - CrstHolder holder(&m_InteropDataCrst); - - // Check to see that it's not already in there - InteropMethodTableData *pDupData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); - if (pDupData != (InteropMethodTableData*) INVALIDENTRY) - return FALSE; - - // Not in there, so insert - m_interopDataHash.InsertValue((UPTR) pMT, (LPVOID) pData); - - // Success - return TRUE; - } -#endif // DACCESS_COMPILE #endif // FEATURE_COMINTEROP void SetDisableInterfaceCache() @@ -2508,7 +2469,6 @@ public: } #ifdef FEATURE_COMINTEROP - ComCallWrapperCache* GetComCallWrapperCache(); RCWCache *GetRCWCache() { WRAPPER_NO_CONTRACT; @@ -2530,12 +2490,6 @@ public: RCWRefCache *GetRCWRefCache(); - void ResetComCallWrapperCache() - { - LIMITED_METHOD_CONTRACT; - m_pComCallWrapperCache = NULL; - } - MethodTable* GetLicenseInteropHelperMethodTable(); #endif // FEATURE_COMINTEROP @@ -3155,10 +3109,6 @@ private: // Hash table that remembers the last cached WinRT factory object per type per appdomain. WinRTFactoryCache *m_pWinRTFactoryCache; - // The wrapper cache for this domain - it has its own CCacheLineAllocator on a per domain basis - // to allow the domain to go away and eventually kill the memory when all refs are gone - ComCallWrapperCache *m_pComCallWrapperCache; - // this cache stores the RCWs in this domain RCWCache *m_pRCWCache; diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp index 9ff186e..eb54b95 100644 --- a/src/vm/ceeload.cpp +++ b/src/vm/ceeload.cpp @@ -829,6 +829,12 @@ BOOL Module::CanCacheWinRTTypeByGuid(MethodTable *pMT) } CONTRACTL_END; + // Don't cache WinRT types in collectible modules. + if (IsCollectible()) + { + return FALSE; + } + // Don't cache mscorlib-internal declarations of WinRT types. if (IsSystem() && pMT->IsProjectedFromWinRT()) return FALSE; diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index 9b3d997..57d1ede 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -1149,13 +1149,6 @@ VOID SimpleComCallWrapper::Neuter(bool fSkipHandleCleanup) // NULL the context, we shall only use m_dwDomainId from this point on m_pContext = NULL; - // NULL the handles of DispatchMemberInfo if the AppDomain host them has been unloaded - DispatchExInfo *pDispatchExInfo = GetDispatchExInfo(); - if (pDispatchExInfo) - { - pDispatchExInfo->DestroyMemberInfoHandles(); - } - StackSString ssMessage; ComCallWrapper *pWrap = m_pWrap; if (g_pConfig->LogCCWRefCountChangeEnabled()) @@ -2242,10 +2235,7 @@ ComCallWrapper* ComCallWrapper::CopyFromTemplate(ComCallWrapperTemplate* pTempla size_t numInterfaces = pTemplate->GetNumInterfaces(); // we have a template, create a wrapper and initialize from the template - // alloc wrapper, aligned 32 bytes - if (pWrapperCache->IsDomainUnloading()) - COMPlusThrow(kAppDomainUnloadedException); - + // alloc wrapper, aligned to cache line NewCCWHolder pStartWrapper(pWrapperCache); pStartWrapper = (ComCallWrapper*)pWrapperCache->GetCacheLineAllocator()-> #ifdef _WIN64 @@ -2588,15 +2578,7 @@ ComCallWrapper* ComCallWrapper::CreateWrapper(OBJECTREF* ppObj, ComCallWrapperTe ComCallWrapperCache *pWrapperCache = NULL; TypeHandle thClass = pServer->GetTrueTypeHandle(); - // - // Collectible types do not support com interop - // - if (thClass.GetMethodTable()->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - - pWrapperCache = pContext->GetDomain()->GetComCallWrapperCache(); + pWrapperCache = thClass.GetMethodTable()->GetLoaderAllocator()->GetComCallWrapperCache(); { // check if somebody beat us to it @@ -3809,7 +3791,7 @@ ComCallWrapperCache::ComCallWrapperCache() : m_lock(CrstCOMWrapperCache), m_cbRef(0), m_pCacheLineAllocator(NULL), - m_pDomain(NULL) + m_pLoaderAllocator(NULL) { WRAPPER_NO_CONTRACT; @@ -3824,26 +3806,25 @@ ComCallWrapperCache::~ComCallWrapperCache() CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::~ComCallWrapperCache %8.8x in domain [%d] %8.8x %S\n", - this, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::~ComCallWrapperCache %8.8x in loader allocator [%d] %8.8x\n", + this, GetLoaderAllocator() ? GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); - if (m_pCacheLineAllocator) + if (m_pCacheLineAllocator) { delete m_pCacheLineAllocator; m_pCacheLineAllocator = NULL; } - AppDomain *pDomain = GetDomain(); // don't use member directly, need to mask off flags - if (pDomain) + LoaderAllocator *pLoaderAllocator = GetLoaderAllocator(); // don't use member directly, need to mask off flags + if (pLoaderAllocator) { - // clear hook in AppDomain as we're going away - pDomain->ResetComCallWrapperCache(); + // clear hook in LoaderAllocator as we're going away + pLoaderAllocator->ResetComCallWrapperCache(); } } @@ -3852,7 +3833,7 @@ ComCallWrapperCache::~ComCallWrapperCache() // ComCallable wrapper manager // Create/Init method //------------------------------------------------------------------- -ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) +ComCallWrapperCache *ComCallWrapperCache::Create(LoaderAllocator *pLoaderAllocator) { CONTRACT (ComCallWrapperCache*) { @@ -3860,19 +3841,19 @@ ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pDomain)); + PRECONDITION(CheckPointer(pLoaderAllocator)); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; NewHolder pWrapperCache = new ComCallWrapperCache(); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Create %8.8x in domain %8.8x %S\n", - (ComCallWrapperCache *)pWrapperCache, pDomain, pDomain->GetFriendlyName(FALSE))); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Create %8.8x in loader allocator [%d] %8.8x\n", + (ComCallWrapperCache *)pWrapperCache, pLoaderAllocator ? pLoaderAllocator->GetCreationNumber() : 0, pLoaderAllocator)); NewHolder line = new CCacheLineAllocator; - pWrapperCache->m_pDomain = pDomain; + pWrapperCache->m_pLoaderAllocator = pLoaderAllocator; pWrapperCache->m_pCacheLineAllocator = line; pWrapperCache->AddRef(); @@ -3882,18 +3863,6 @@ ComCallWrapperCache *ComCallWrapperCache::Create(AppDomain *pDomain) RETURN pWrapperCache; } - -void ComCallWrapperCache::Neuter() -{ - WRAPPER_NO_CONTRACT; - - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Neuter %8.8x in domain [%d] %8.8x %S\n", - this, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(),GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); - ClearDomain(); -} - - //------------------------------------------------------------------- // ComCallable wrapper manager // LONG AddRef() @@ -3911,9 +3880,8 @@ LONG ComCallWrapperCache::AddRef() COUNTER_ONLY(GetPerfCounters().m_Interop.cCCW++); LONG i = FastInterlockIncrement(&m_cbRef); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Addref %8.8x with %d in domain [%d] %8.8x %S\n", - this, i, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Addref %8.8x with %d in loader allocator [%d] %8.8x\n", + this, i, GetLoaderAllocator()?GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); return i; } @@ -3937,9 +3905,8 @@ LONG ComCallWrapperCache::Release() LONG i = FastInterlockDecrement(&m_cbRef); _ASSERTE(i >= 0); - LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Release %8.8x with %d in domain [%d] %8.8x %S\n", - this, i, GetDomain()?GetDomain()->GetId().m_dwId:0, - GetDomain(), GetDomain() ? GetDomain()->GetFriendlyNameForLogging() : NULL)); + LOG((LF_INTEROP, LL_INFO100, "ComCallWrapperCache::Release %8.8x with %d in loader allocator [%d] %8.8x\n", + this, i, GetLoaderAllocator() ? GetLoaderAllocator()->GetCreationNumber() : 0, GetLoaderAllocator())); if ( i == 0) delete this; diff --git a/src/vm/comcallablewrapper.h b/src/vm/comcallablewrapper.h index 56ebc94..e58b4c8 100644 --- a/src/vm/comcallablewrapper.h +++ b/src/vm/comcallablewrapper.h @@ -41,11 +41,6 @@ typedef DPTR(struct SimpleComCallWrapper) PTR_SimpleComCallWrapper; class ComCallWrapperCache { - enum - { - AD_IS_UNLOADING = 0x01, - }; - public: // Encapsulate a SpinLockHolder, so that clients of our lock don't have to know // the details of our implementation. @@ -62,12 +57,8 @@ public: ComCallWrapperCache(); ~ComCallWrapperCache(); - // create a new WrapperCache (one per domain) - static ComCallWrapperCache* Create(AppDomain *pDomain); - - // Called when the domain is going away. We may have outstanding references to this cache, - // so we keep it around in a neutered state. - void Neuter(); + // create a new WrapperCache (one per each LoaderAllocator) + static ComCallWrapperCache* Create(LoaderAllocator *pLoaderAllocator); // refcount LONG AddRef(); @@ -87,9 +78,9 @@ public: RETURN m_pCacheLineAllocator; } - AppDomain* GetDomain() + LoaderAllocator* GetLoaderAllocator() { - CONTRACT (AppDomain*) + CONTRACT (LoaderAllocator*) { WRAPPER(THROWS); WRAPPER(GC_TRIGGERS); @@ -98,41 +89,13 @@ public: } CONTRACT_END; - RETURN ((AppDomain*)((size_t)m_pDomain & ~AD_IS_UNLOADING)); - } - - void ClearDomain() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain *)AD_IS_UNLOADING; - } - - void SetDomainIsUnloading() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain*)((size_t)m_pDomain | AD_IS_UNLOADING); - } - - void ResetDomainIsUnloading() - { - LIMITED_METHOD_CONTRACT; - - m_pDomain = (AppDomain*)((size_t)m_pDomain & (~AD_IS_UNLOADING)); - } - - BOOL IsDomainUnloading() - { - LIMITED_METHOD_CONTRACT; - - return ((size_t)m_pDomain & AD_IS_UNLOADING) != 0; + RETURN m_pLoaderAllocator; } private: LONG m_cbRef; CCacheLineAllocator* m_pCacheLineAllocator; - AppDomain* m_pDomain; + LoaderAllocator* m_pLoaderAllocator; // spin lock for fast synchronization Crst m_lock; diff --git a/src/vm/comtoclrcall.h b/src/vm/comtoclrcall.h index d2f3891..5425fc0 100644 --- a/src/vm/comtoclrcall.h +++ b/src/vm/comtoclrcall.h @@ -402,7 +402,7 @@ public: CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/vm/dispatchinfo.cpp b/src/vm/dispatchinfo.cpp index fe79ac6..1b4fbb4 100644 --- a/src/vm/dispatchinfo.cpp +++ b/src/vm/dispatchinfo.cpp @@ -137,10 +137,6 @@ DispatchMemberInfo::~DispatchMemberInfo() if (m_pParamInOnly) delete [] m_pParamInOnly; - // Destroy the member info object. - if (m_hndMemberInfo) - DestroyHandle(m_hndMemberInfo); - // Clear the name of the member. m_strName.Clear(); } @@ -307,14 +303,14 @@ PTRARRAYREF DispatchMemberInfo::GetParameters() { case Method: { - pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); break; } case Property: { - pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); break; } @@ -323,14 +319,17 @@ PTRARRAYREF DispatchMemberInfo::GetParameters() // If the member has parameters then retrieve the array of parameters. if (pGetParamsMD != NULL) { - MethodDescCallSite getParams(pGetParamsMD, m_hndMemberInfo); + OBJECTREF memberInfoObject = GetMemberInfoObject(); + GCPROTECT_BEGIN(memberInfoObject) + MethodDescCallSite getParams(pGetParamsMD, &memberInfoObject); ARG_SLOT GetParamsArgs[] = { - ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + ObjToArgSlot(memberInfoObject) }; ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); + GCPROTECT_END(); } return ParamArray; @@ -565,7 +564,7 @@ void DispatchMemberInfo::DetermineMemberType() } CONTRACTL_END; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); // Check to see if the member info is of a type we have already seen. TypeHandle pMemberInfoClass = MemberInfoObj->GetTypeHandle(); @@ -617,7 +616,7 @@ void DispatchMemberInfo::DetermineParamCount() MethodDesc *pGetParamsMD = NULL; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(MemberInfoObj); { // Retrieve the method to use to retrieve the array of parameters. @@ -625,14 +624,14 @@ void DispatchMemberInfo::DetermineParamCount() { case Method: { - pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); break; } case Property: { - pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, GetMemberInfoObject()->GetTypeHandle()); _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); break; } @@ -645,7 +644,7 @@ void DispatchMemberInfo::DetermineParamCount() ARG_SLOT GetParamsArgs[] = { - ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + ObjToArgSlot(GetMemberInfoObject()) }; PTRARRAYREF ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); @@ -676,7 +675,7 @@ void DispatchMemberInfo::DetermineCultureAwareness() MethodTable * pLcIdConvAttrClass = MscorlibBinder::GetClass(CLASS__LCID_CONVERSION_TYPE); // Check to see if the attribute is set. - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(MemberInfoObj); { // Retrieve the method to use to determine if the DispIdAttribute custom attribute is set. @@ -732,7 +731,7 @@ void DispatchMemberInfo::SetUpParamMarshalerInfo() BOOL bSetUpReturnValueOnly = FALSE; OBJECTREF SetterObj = NULL; OBJECTREF GetterObj = NULL; - OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + OBJECTREF MemberInfoObj = GetMemberInfoObject(); GCPROTECT_BEGIN(SetterObj); GCPROTECT_BEGIN(GetterObj); @@ -1024,6 +1023,18 @@ void DispatchMemberInfo::SetUpDispParamAttributes(int iParam, MarshalInfo* Info) m_pParamInOnly[iParam] = ( Info->IsIn() && !Info->IsOut() ); } +#ifndef DACCESS_COMPILE +OBJECTREF DispatchMemberInfo::GetMemberInfoObject() +{ + return m_pDispInfo->GetLoaderAllocator()->GetHandleValue(m_hndMemberInfo); +} + +void DispatchMemberInfo::ClearMemberInfoObject() +{ + m_pDispInfo->GetLoaderAllocator()->SetHandleValue(m_hndMemberInfo, NULL); +} +#endif // DACCESS_COMPILE + //-------------------------------------------------------------------------------- // The DispatchInfo class implementation. @@ -1125,7 +1136,7 @@ DispatchMemberInfo* DispatchInfo::FindMember(SString& strName, BOOL bCaseSensiti DispatchMemberInfo *pCurrMemberInfo = m_pFirstMemberInfo; while (pCurrMemberInfo) { - if (ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo) != NULL) + if (pCurrMemberInfo->GetMemberInfoObject() != NULL) { // Compare the 2 strings. if (bCaseSensitive ? @@ -1162,7 +1173,7 @@ DispatchMemberInfo* DispatchInfo::CreateDispatchMemberInfoInstance(DISPID DispID CONTRACT_END; DispatchMemberInfo* pInfo = new DispatchMemberInfo(this, DispID, strMemberName, MemberInfoObj); - pInfo->SetHandle(MemberInfoObj->GetMethodTable()->GetDomain()->CreateHandle(MemberInfoObj)); + pInfo->SetHandle(GetLoaderAllocator()->AllocateHandle(MemberInfoObj)); RETURN pInfo; } @@ -1584,7 +1595,7 @@ void DispatchInfo::InvokeMemberWorker(DispatchMemberInfo* pDispMemberInfo, } // Retrieve the member info object and the type of the member. - pObjs->MemberInfo = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + pObjs->MemberInfo = pDispMemberInfo->GetMemberInfoObject(); MemberType = pDispMemberInfo->GetMemberType(); switch (MemberType) @@ -2056,7 +2067,7 @@ HRESULT DispatchInfo::InvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, // DispatchMemberInfo *pDispMemberInfo = FindMember(id); - if (!pDispMemberInfo || !(*((Object **)pDispMemberInfo->m_hndMemberInfo))) + if (!pDispMemberInfo || !pDispMemberInfo->GetMemberInfoObject()) { pDispMemberInfo = NULL; } @@ -2288,27 +2299,6 @@ HRESULT DispatchInfo::InvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, return hr; } -void DispatchInfo::DestroyMemberInfoHandles() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - DispatchMemberInfo* pCurrMember = m_pFirstMemberInfo; - while (pCurrMember) - { - // Destroy the handle - DestroyHandle(pCurrMember->m_hndMemberInfo); - pCurrMember->m_hndMemberInfo = NULL; - // Process the next member. - pCurrMember = pCurrMember->m_pNext; - } -} - // Parameter marshaling helpers. void DispatchInfo::MarshalParamNativeToManaged(DispatchMemberInfo *pMemberInfo, int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj) { @@ -2793,7 +2783,7 @@ BOOL DispatchInfo::SynchWithManagedView() while (pCurrMemberInfo) { // We can simply compare the OBJECTREF's. - if (CurrMemberInfoObj == ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo)) + if (CurrMemberInfoObj == pCurrMemberInfo->GetMemberInfoObject()) { // We have found a match. bMatch = TRUE; @@ -3317,7 +3307,7 @@ DispatchMemberInfo* DispatchExInfo::CreateDispatchMemberInfoInstance(DISPID Disp AppDomain* pDomain = SystemDomain::GetAppDomainFromId(m_pSimpleWrapperOwner->GetDomainID(), ADV_CURRENTAD); - pInfo->SetHandle(pDomain->CreateHandle(MemberInfoObj)); + pInfo->SetHandle(GetLoaderAllocator()->AllocateHandle(MemberInfoObj)); RETURN pInfo; } @@ -3352,7 +3342,7 @@ DispatchMemberInfo* DispatchExInfo::GetFirstMember() } // Now we need to make sure we skip any members that are deleted. - while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + while ((*ppNextMemberInfo) && !(*ppNextMemberInfo)->GetMemberInfoObject()) ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); RETURN *ppNextMemberInfo; @@ -3392,7 +3382,7 @@ DispatchMemberInfo* DispatchExInfo::GetNextMember(DISPID CurrMemberDispID) } // Now we need to make sure we skip any members that are deleted. - while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + while ((*ppNextMemberInfo) && !(*ppNextMemberInfo)->GetMemberInfoObject()) ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); RETURN *ppNextMemberInfo; @@ -3501,7 +3491,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) CrstHolder ch(&m_lock); // If the member does not exist, it is static or has been deleted then we have nothing more to do. - if (pDispMemberInfo && (ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo) != NULL)) + if (pDispMemberInfo && (pDispMemberInfo->GetMemberInfoObject() != NULL)) { OBJECTREF TargetObj = GetReflectionObject(); GCPROTECT_BEGIN(TargetObj); @@ -3510,7 +3500,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) MethodDesc *pMD = GetIExpandoMD(METHOD__IEXPANDO__REMOVE_MEMBER); MethodDescCallSite removeMember(pMD, &TargetObj); - OBJECTREF MemberInfoObj = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + OBJECTREF MemberInfoObj = pDispMemberInfo->GetMemberInfoObject(); // Prepare the arguments that will be passed to RemoveMember. ARG_SLOT Args[] = @@ -3523,7 +3513,7 @@ void DispatchExInfo::DeleteMember(DISPID DispID) removeMember.Call(Args); // Set the handle to point to NULL to indicate the member has been removed. - StoreObjectInHandle(pDispMemberInfo->m_hndMemberInfo, NULL); + pDispMemberInfo->ClearMemberInfoObject(); GCPROTECT_END(); } diff --git a/src/vm/dispatchinfo.h b/src/vm/dispatchinfo.h index 97b4ea7..f5f9b9b 100644 --- a/src/vm/dispatchinfo.h +++ b/src/vm/dispatchinfo.h @@ -138,7 +138,7 @@ struct DispatchMemberInfo return m_bLastParamOleVarArg; } - void SetHandle(OBJECTHANDLE objhnd) + void SetHandle(LOADERHANDLE objhnd) { m_hndMemberInfo = objhnd; } @@ -149,6 +149,11 @@ struct DispatchMemberInfo return m_bRequiresManagedCleanup; } +#ifndef DACCESS_COMPILE + OBJECTREF GetMemberInfoObject(); + void ClearMemberInfoObject(); +#endif // DACCESS_COMPILE + // Parameter marshaling methods. void MarshalParamNativeToManaged(int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj); void MarshalParamManagedToNativeRef(int iParam, OBJECTREF *pSrcObj, VARIANT *pRefVar); @@ -174,7 +179,7 @@ private: void SetUpDispParamAttributes(int iParam, MarshalInfo* Info); public: DISPID m_DispID; - OBJECTHANDLE m_hndMemberInfo; + LOADERHANDLE m_hndMemberInfo; DispParamMarshaler** m_apParamMarshaler; BOOL* m_pParamInOnly; DispatchMemberInfo* m_pNext; @@ -276,9 +281,6 @@ public: int* pManagedMethodParamIndexMap, VARIANT** aByrefArgOleVariant); - // Method to NULL the handles inside DispatchMemberInfo - void DestroyMemberInfoHandles(); - // Methods to retrieve the cached MD's static MethodDesc* GetFieldInfoMD(BinderMethodID Method, TypeHandle hndFieldInfoType); static MethodDesc* GetPropertyInfoMD(BinderMethodID Method, TypeHandle hndPropInfoType); @@ -296,6 +298,11 @@ public: // Returns TRUE if the argument is "Missing" static BOOL VariantIsMissing(VARIANT *pOle); + LoaderAllocator* GetLoaderAllocator() + { + return m_pMT->GetLoaderAllocator(); + } + protected: // Parameter marshaling helpers. void MarshalParamNativeToManaged(DispatchMemberInfo *pMemberInfo, int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj); diff --git a/src/vm/interoputil.cpp b/src/vm/interoputil.cpp index 8bb08e4..510e494 100644 --- a/src/vm/interoputil.cpp +++ b/src/vm/interoputil.cpp @@ -3771,10 +3771,6 @@ BOOL IsTypeVisibleFromCom(TypeHandle hndType) return FALSE; } - // If the type is collectible, then it is not visible from COM. - if (hndType.GetLoaderAllocator()->IsCollectible()) - return FALSE; - return SpecialIsGenericTypeVisibleFromCom(hndType); } @@ -5095,11 +5091,11 @@ ClassFactoryBase *GetComClassFactory(MethodTable* pClassMT) if (pClsFac == NULL) { // - // Collectible types do not support com interop + // Collectible types do not support WinRT interop // - if (pClassMT->Collectible()) + if (pClassMT->Collectible() && (pClassMT->IsExportedToWinRT() || pClassMT->IsProjectedFromWinRT())) { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); + COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleWinRT")); } NewHolder pNewFactory; diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp index 314f5cb..d51af51 100644 --- a/src/vm/loaderallocator.cpp +++ b/src/vm/loaderallocator.cpp @@ -10,6 +10,7 @@ #ifndef DACCESS_COMPILE #include "comdelegate.h" #endif +#include "comcallablewrapper.h" //***************************************************************************** // Used by LoaderAllocator::Init for easier readability. @@ -72,6 +73,10 @@ LoaderAllocator::LoaderAllocator() m_pJumpStubCache = NULL; m_IsCollectible = false; +#ifdef FEATURE_COMINTEROP + m_pComCallWrapperCache = NULL; +#endif + m_pUMEntryThunkCache = NULL; m_nLoaderAllocator = InterlockedIncrement64((LONGLONG *)&LoaderAllocator::cLoaderAllocatorsCreated); @@ -657,6 +662,27 @@ BOOL QCALLTYPE LoaderAllocator::Destroy(QCall::LoaderAllocatorHandle pLoaderAllo // This will probably change for shared code unloading _ASSERTE(pID->GetType() == LAT_Assembly); +#ifdef FEATURE_COMINTEROP + if (pLoaderAllocator->m_pComCallWrapperCache) + { + pLoaderAllocator->m_pComCallWrapperCache->Release(); + + // if the above released the wrapper cache, then it will call back and reset our + // m_pComCallWrapperCache to null. + if (!pLoaderAllocator->m_pComCallWrapperCache) + { + LOG((LF_CLASSLOADER, LL_INFO10, "LoaderAllocator::Destroy ComCallWrapperCache released\n")); + } + #ifdef _DEBUG + else + { + pLoaderAllocator->m_pComCallWrapperCache = NULL; + LOG((LF_CLASSLOADER, LL_INFO10, "LoaderAllocator::Destroy ComCallWrapperCache not released\n")); + } + #endif // _DEBUG + } +#endif // FEATURE_COMINTEROP + DomainAssembly* pDomainAssembly = (DomainAssembly*)(pID->GetDomainAssemblyIterator()); if (pDomainAssembly != NULL) { @@ -1043,6 +1069,10 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) m_pDomain = pDomain; m_crstLoaderAllocator.Init(CrstLoaderAllocator, (CrstFlags)CRST_UNSAFE_COOPGC); +#ifdef FEATURE_COMINTEROP + m_InteropDataCrst.Init(CrstInteropData, CRST_REENTRANCY); + m_ComCallWrapperCrst.Init(CrstCOMCallWrapper); +#endif // // Initialize the heaps @@ -1189,6 +1219,14 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) // Set up the IL stub cache m_ILStubCache.Init(m_pHighFrequencyHeap); + +#ifdef FEATURE_COMINTEROP + // Init the COM Interop data hash + { + LockOwner lock = { &m_InteropDataCrst, IsOwnerOfCrst }; + m_interopDataHash.Init(0, NULL, false, &lock); + } +#endif // FEATURE_COMINTEROP } @@ -1295,6 +1333,10 @@ void LoaderAllocator::Terminate() m_pUMEntryThunkCache = NULL; m_crstLoaderAllocator.Destroy(); +#ifdef FEATURE_COMINTEROP + m_ComCallWrapperCrst.Destroy(); + m_InteropDataCrst.Destroy(); +#endif m_LoaderAllocatorReferences.RemoveAll(); // In collectible types we merge the low frequency and high frequency heaps @@ -1858,6 +1900,30 @@ void AssemblyLoaderAllocator::ReleaseManagedAssemblyLoadContext() } } +#ifdef FEATURE_COMINTEROP +ComCallWrapperCache * LoaderAllocator::GetComCallWrapperCache() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (!m_pComCallWrapperCache) + { + CrstHolder lh(&m_ComCallWrapperCrst); + + if (!m_pComCallWrapperCache) + m_pComCallWrapperCache = ComCallWrapperCache::Create(this); + } + _ASSERTE(m_pComCallWrapperCache); + return m_pComCallWrapperCache; +} +#endif // FEATURE_COMINTEROP + // U->M thunks created in this LoaderAllocator and not associated with a delegate. UMEntryThunkCache *LoaderAllocator::GetUMEntryThunkCache() { @@ -1886,4 +1952,47 @@ UMEntryThunkCache *LoaderAllocator::GetUMEntryThunkCache() #endif // !CROSSGEN_COMPILE +#ifdef FEATURE_COMINTEROP + +// Look up interop data for a method table +// Returns the data pointer if present, NULL otherwise +InteropMethodTableData *LoaderAllocator::LookupComInteropData(MethodTable *pMT) +{ + // Take the lock + CrstHolder holder(&m_InteropDataCrst); + + // Lookup + InteropMethodTableData *pData = (InteropMethodTableData*)m_interopDataHash.LookupValue((UPTR)pMT, (LPVOID)NULL); + + // Not there... + if (pData == (InteropMethodTableData*)INVALIDENTRY) + return NULL; + + // Found it + return pData; +} + +// Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry +BOOL LoaderAllocator::InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData) +{ + // We don't keep track of this kind of information for interfaces + _ASSERTE(!pMT->IsInterface()); + + // Take the lock + CrstHolder holder(&m_InteropDataCrst); + + // Check to see that it's not already in there + InteropMethodTableData *pDupData = (InteropMethodTableData*)m_interopDataHash.LookupValue((UPTR)pMT, (LPVOID)NULL); + if (pDupData != (InteropMethodTableData*)INVALIDENTRY) + return FALSE; + + // Not in there, so insert + m_interopDataHash.InsertValue((UPTR)pMT, (LPVOID)pData); + + // Success + return TRUE; +} + +#endif // FEATURE_COMINTEROP + #endif // !DACCESS_COMPILE diff --git a/src/vm/loaderallocator.hpp b/src/vm/loaderallocator.hpp index f2d7290..a283721 100644 --- a/src/vm/loaderallocator.hpp +++ b/src/vm/loaderallocator.hpp @@ -129,6 +129,10 @@ class ListLockEntryBase; typedef ListLockEntryBase ListLockEntry; class UMEntryThunkCache; +#ifdef FEATURE_COMINTEROP +class ComCallWrapperCache; +#endif // FEATURE_COMINTEROP + class LoaderAllocator { VPTR_BASE_VTABLE_CLASS(LoaderAllocator) @@ -248,6 +252,18 @@ private: SList m_failedTypeInitCleanupList; SegmentedHandleIndexStack m_freeHandleIndexesStack; +#ifdef FEATURE_COMINTEROP + // The wrapper cache for this loader allocator - it has its own CCacheLineAllocator on a per loader allocator basis + // to allow the loader allocator to go away and eventually kill the memory when all refs are gone + + VolatilePtr m_pComCallWrapperCache; + // Used for synchronizing creation of the m_pComCallWrapperCache + CrstExplicitInit m_ComCallWrapperCrst; + // Hash table that maps a MethodTable to COM Interop compatibility data. + PtrHashMap m_interopDataHash; + // Used for synchronizing access to the m_interopDataHash + CrstExplicitInit m_InteropDataCrst; +#endif #ifndef DACCESS_COMPILE @@ -509,6 +525,7 @@ public: void InitVirtualCallStubManager(BaseDomain *pDomain); void UninitVirtualCallStubManager(); + #ifndef CROSSGEN_COMPILE inline VirtualCallStubManager *GetVirtualCallStubManager() { @@ -530,6 +547,30 @@ public: LIMITED_METHOD_CONTRACT; return &m_ILStubCache; } + +#ifdef FEATURE_COMINTEROP + + ComCallWrapperCache * GetComCallWrapperCache(); + + void ResetComCallWrapperCache() + { + LIMITED_METHOD_CONTRACT; + m_pComCallWrapperCache = NULL; + } + +#ifndef DACCESS_COMPILE + + // Look up interop data for a method table + // Returns the data pointer if present, NULL otherwise + InteropMethodTableData *LookupComInteropData(MethodTable *pMT); + + // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry + BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData); + +#endif // DACCESS_COMPILE + +#endif // FEATURE_COMINTEROP + }; // class LoaderAllocator typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index 5ed91d1..cf7ddcc 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -8313,7 +8313,8 @@ Instantiation MethodTable::GetInstantiationOfParentClass(MethodTable *pWhichPare InteropMethodTableData *MethodTable::LookupComInteropData() { WRAPPER_NO_CONTRACT; - return GetDomain()->LookupComInteropData(this); + + return GetLoaderAllocator()->LookupComInteropData(this); } //========================================================================================== @@ -8321,7 +8322,8 @@ InteropMethodTableData *MethodTable::LookupComInteropData() BOOL MethodTable::InsertComInteropData(InteropMethodTableData *pData) { WRAPPER_NO_CONTRACT; - return GetDomain()->InsertComInteropData(this, pData); + + return GetLoaderAllocator()->InsertComInteropData(this, pData); } //========================================================================================== diff --git a/src/vm/runtimecallablewrapper.cpp b/src/vm/runtimecallablewrapper.cpp index 0832b56..6280c2c 100644 --- a/src/vm/runtimecallablewrapper.cpp +++ b/src/vm/runtimecallablewrapper.cpp @@ -629,7 +629,6 @@ void ComClassFactory::Init(__in_opt WCHAR* pwszProgID, __in_opt WCHAR* pwszServe m_pwszProgID = pwszProgID; m_pwszServer = pwszServer; - _ASSERTE(pClassMT == NULL || !pClassMT->Collectible()); m_pClassMT = pClassMT; } @@ -2959,14 +2958,6 @@ IUnknown* RCW::GetComIPFromRCW(MethodTable* pMT) RETURN result; } - // - // Collectible types do not support com interop - // - if (pMT->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - // returns an AddRef'ed IP RETURN GetComIPForMethodTableFromCache(pMT); } @@ -4739,15 +4730,6 @@ OBJECTREF ComObject::CreateComObjectRef(MethodTable* pMT) { pMT->CheckRestore(); pMT->EnsureInstanceActive(); - - // - // Collectible types do not support com interop - // - if (pMT->Collectible()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); - } - pMT->CheckRunClassInitThrowing(); } diff --git a/src/vm/stdinterfaces.cpp b/src/vm/stdinterfaces.cpp index cfcfd43..b96681f 100644 --- a/src/vm/stdinterfaces.cpp +++ b/src/vm/stdinterfaces.cpp @@ -1948,7 +1948,7 @@ HRESULT __stdcall DispatchEx_GetMemberName ( DispatchMemberInfo *pDispMemberInfo = pDispExInfo->SynchFindMember(id); // If the member does not exist then we return DISP_E_MEMBERNOTFOUND. - if (!pDispMemberInfo || !ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo)) + if (!pDispMemberInfo || !pDispMemberInfo->GetMemberInfoObject()) { hr = DISP_E_MEMBERNOTFOUND; } @@ -2008,7 +2008,7 @@ HRESULT __stdcall DispatchEx_GetMemberProperties ( DispatchMemberInfo *pDispMemberInfo = pDispExInfo->SynchFindMember(id); // If the member does not exist then we return DISP_E_MEMBERNOTFOUND. - if (!pDispMemberInfo || (MemberInfoObj = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo)) == NULL) + if (!pDispMemberInfo || (MemberInfoObj = pDispMemberInfo->GetMemberInfoObject()) == NULL) { hr = DISP_E_MEMBERNOTFOUND; } diff --git a/tests/issues.targets b/tests/issues.targets index 9271921..308f10e 100644 --- a/tests/issues.targets +++ b/tests/issues.targets @@ -324,6 +324,9 @@ 20682 + + 20682 + 20682 diff --git a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs index d1ef171..4a27948 100644 --- a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs +++ b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs @@ -10,6 +10,7 @@ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Loader; using System.Security; using System.Text; using System.Threading; @@ -303,5 +304,64 @@ namespace TestLibrary public static IntPtr HKEY_LOCAL_MACHINE => new IntPtr(unchecked((int)0x80000002)); } + + class TestAssemblyLoadContext : AssemblyLoadContext + { + public TestAssemblyLoadContext() : base(isCollectible: true) + { + + } + + protected override Assembly Load(AssemblyName assemblyName) + { + return null; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int ExecuteAndUnloadInternal(string assemblyPath, string[] args, Action unloadingCallback, out WeakReference alcWeakRef) + { + TestAssemblyLoadContext alc = new TestAssemblyLoadContext(); + if (unloadingCallback != null) + { + alc.Unloading += unloadingCallback; + } + alcWeakRef = new WeakReference(alc); + + Assembly a = alc.LoadFromAssemblyPath(assemblyPath); + + object[] argsObjArray = (a.EntryPoint.GetParameters().Length != 0) ? new object[] { args } : null; + object res = a.EntryPoint.Invoke(null, argsObjArray); + + alc.Unload(); + + return (a.EntryPoint.ReturnType == typeof(void)) ? Environment.ExitCode : Convert.ToInt32(res); + } + + public static int ExecuteAndUnload(string assemblyPath, string[] args, Action unloadingCallback = null) + { + WeakReference alcWeakRef; + int exitCode; + + exitCode = ExecuteAndUnloadInternal(assemblyPath, args, unloadingCallback, out alcWeakRef); + + for (int i = 0; i < 8 && alcWeakRef.IsAlive; i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + if (alcWeakRef.IsAlive) + { + exitCode += 100; + Console.WriteLine("Unload failed"); + } + else + { + Console.WriteLine("Unload succeeded"); + } + + return exitCode; + } } } diff --git a/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj new file mode 100644 index 0000000..4840931 --- /dev/null +++ b/tests/src/Interop/COM/NETClients/Primitives/NETClientPrimitivesInALC.csproj @@ -0,0 +1,38 @@ + + + + + Debug + AnyCPU + NETClientPrimitivesInALC + 2.0 + {85C57688-DA98-4DE3-AC9B-526E4747434C} + Exe + {209912F9-0DA1-4184-9CC1-8D583BAF4A28};{87799F5D-CEBD-499D-BDBA-B2C6105CD766} + App.manifest + + + BuildOnly + + + false + + + true + true + + + + + + + + + + + + + + + + diff --git a/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs b/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs new file mode 100644 index 0000000..eb5e9dd --- /dev/null +++ b/tests/src/Interop/COM/NETClients/Primitives/TestInALC.cs @@ -0,0 +1,19 @@ +// 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 System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "NETClientPrimitives.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} diff --git a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj index ddcd2c3..855cb06 100644 --- a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj +++ b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTest.csproj @@ -28,7 +28,7 @@ - + diff --git a/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj new file mode 100644 index 0000000..26824ed --- /dev/null +++ b/tests/src/Interop/MarshalAPI/IUnknown/IUnknownTestInALC.csproj @@ -0,0 +1,45 @@ + + + + + Debug + AnyCPU + IUnknownTestInALC + 2.0 + {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + $(DefineConstants);STATIC + + + true + true + + + + + + + + + False + + + + + + + + + + + {c8c0dc74-fac4-45b1-81fe-70c4808366e0} + CoreCLRTestLibrary + + + + + + diff --git a/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs b/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs new file mode 100644 index 0000000..c14766a --- /dev/null +++ b/tests/src/Interop/MarshalAPI/IUnknown/TestInALC.cs @@ -0,0 +1,19 @@ +// 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 System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "IUnknownTest.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} diff --git a/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj new file mode 100644 index 0000000..f197d1a --- /dev/null +++ b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/DefaultTestInALC.csproj @@ -0,0 +1,38 @@ + + + + + Debug + AnyCPU + DefaultTestInALC + 2.0 + {43531C46-AFE2-4254-93C6-7F17E30D750C} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\..\ + $(DefineConstants);STATIC + + true + true + + + + + + + False + + + + + + + + + + + + + + + diff --git a/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs new file mode 100644 index 0000000..265d70f --- /dev/null +++ b/tests/src/Interop/PInvoke/NativeCallManagedComVisible/Default/TestInALC.cs @@ -0,0 +1,19 @@ +// 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 System.IO; +using System.Reflection; + +namespace TestInALC +{ + class Test + { + static int Main(string[] args) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "DefaultTest.exe"); + return TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args); + } + } +} -- 2.7.4