diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 4a4520d56e219..813616e529c80 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -162,6 +162,7 @@ #include "disassembler.h" #include "jithost.h" #include "pgo.h" +#include "pendingload.h" #ifndef TARGET_UNIX #include "dwreport.h" @@ -623,6 +624,8 @@ void EEStartupHelper() // This needs to be done before the EE has started InitializeStartupFlags(); + PendingTypeLoadTable::Init(); + IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); Thread::StaticInitialize(); diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 432b647a04e15..a98a57b7b8e25 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -216,7 +216,7 @@ Module * ClassLoader::ComputeLoaderModule(MethodTable * pMT, methodInst); } /*static*/ -Module *ClassLoader::ComputeLoaderModule(TypeKey *typeKey) +Module *ClassLoader::ComputeLoaderModule(const TypeKey *typeKey) { CONTRACTL { @@ -729,7 +729,7 @@ void ClassLoader::LazyPopulateCaseInsensitiveHashTables() } /*static*/ -void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(TypeKey *pKey, +void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(const TypeKey *pKey, UINT resIDWhy) { STATIC_CONTRACT_THROWS; @@ -743,7 +743,7 @@ void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(TypeKey *pKey, #endif -TypeHandle ClassLoader::LoadConstructedTypeThrowing(TypeKey *pKey, +TypeHandle ClassLoader::LoadConstructedTypeThrowing(const TypeKey *pKey, LoadTypesFlag fLoadTypes /*= LoadTypes*/, ClassLoadLevel level /*=CLASS_LOADED*/, const InstantiationContext *pInstContext /*=NULL*/) @@ -858,7 +858,7 @@ void ClassLoader::TryEnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level) } /* static */ -TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable) +TypeHandle ClassLoader::LookupTypeKey(const TypeKey *pKey, EETypeHashTable *pTable) { CONTRACTL { NOTHROW; @@ -875,7 +875,7 @@ TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable) } /* static */ -TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey) +TypeHandle ClassLoader::LookupInLoaderModule(const TypeKey *pKey) { CONTRACTL { NOTHROW; @@ -895,7 +895,7 @@ TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey) /* static */ -TypeHandle ClassLoader::LookupTypeHandleForTypeKey(TypeKey *pKey) +TypeHandle ClassLoader::LookupTypeHandleForTypeKey(const TypeKey *pKey) { CONTRACTL { @@ -1659,12 +1659,6 @@ ClassLoader::~ClassLoader() } CONTRACTL_END -#ifdef _DEBUG - // Do not walk m_pUnresolvedClassHash at destruct time as it is loaderheap allocated memory - // and may already have been deallocated via an AllocMemTracker. - m_pUnresolvedClassHash = (PendingTypeLoadTable*)(UINT_PTR)0xcccccccc; -#endif - #ifdef _DEBUG // LOG(( // LF_CLASSLOADER, @@ -1704,7 +1698,6 @@ ClassLoader::~ClassLoader() FreeModules(); - m_UnresolvedClassLock.Destroy(); m_AvailableClassLock.Destroy(); m_AvailableTypesLock.Destroy(); } @@ -1729,7 +1722,6 @@ ClassLoader::ClassLoader(Assembly *pAssembly) m_pAssembly = pAssembly; - m_pUnresolvedClassHash = NULL; m_cUnhashedModules = 0; #ifdef _DEBUG @@ -1760,12 +1752,6 @@ VOID ClassLoader::Init(AllocMemTracker *pamTracker) { STANDARD_VM_CONTRACT; - m_pUnresolvedClassHash = PendingTypeLoadTable::Create(GetAssembly()->GetLowFrequencyHeap(), - UNRESOLVED_CLASS_HASH_BUCKETS, - pamTracker); - - m_UnresolvedClassLock.Init(CrstUnresolvedClassLock); - // This lock is taken within the classloader whenever we have to enter a // type in one of the modules governed by the loader. // The process of creating these types may be reentrant. The ordering has @@ -2605,7 +2591,7 @@ ClassLoader::LoadApproxParentThrowing( // Perform a single phase of class loading // It is the caller's responsibility to lock /*static*/ -TypeHandle ClassLoader::DoIncrementalLoad(TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel currentLevel) +TypeHandle ClassLoader::DoIncrementalLoad(const TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel currentLevel) { CONTRACTL { @@ -2679,7 +2665,7 @@ TypeHandle ClassLoader::DoIncrementalLoad(TypeKey *pTypeKey, TypeHandle typeHnd, // For canonical instantiations of generic types, create a brand new method table // For other constructed types, create a type desc and template method table if necessary // For all other types, create a method table -TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracker* pamTracker) +TypeHandle ClassLoader::CreateTypeHandleForTypeKey(const TypeKey* pKey, AllocMemTracker* pamTracker) { CONTRACT(TypeHandle) { @@ -2794,7 +2780,7 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke // particular, exact parent info (base class and interfaces) is loaded // in a later phase /*static*/ -TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd) +TypeHandle ClassLoader::PublishType(const TypeKey *pTypeKey, TypeHandle typeHnd) { CONTRACTL { @@ -2994,7 +2980,7 @@ static void PushFinalLevels(TypeHandle typeHnd, ClassLoadLevel targetLevel, cons // -TypeHandle ClassLoader::LoadTypeHandleForTypeKey(TypeKey *pTypeKey, +TypeHandle ClassLoader::LoadTypeHandleForTypeKey(const TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel targetLevel/*=CLASS_LOADED*/, const InstantiationContext *pInstContext/*=NULL*/) @@ -3018,8 +3004,7 @@ TypeHandle ClassLoader::LoadTypeHandleForTypeKey(TypeKey *pTypeKey, SString name; TypeString::AppendTypeKeyDebug(name, pTypeKey); LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: LoadTypeHandleForTypeKey for type %s to level %s\n", name.GetUTF8(), classLoadLevelName[targetLevel])); - CrstHolder unresolvedClassLockHolder(&m_UnresolvedClassLock); - m_pUnresolvedClassHash->Dump(); + PendingTypeLoadTable::GetTable()->Dump(); } #endif @@ -3051,7 +3036,7 @@ TypeHandle ClassLoader::LoadTypeHandleForTypeKey(TypeKey *pTypeKey, } // -TypeHandle ClassLoader::LoadTypeHandleForTypeKeyNoLock(TypeKey *pTypeKey, +TypeHandle ClassLoader::LoadTypeHandleForTypeKeyNoLock(const TypeKey *pTypeKey, ClassLoadLevel targetLevel/*=CLASS_LOADED*/, const InstantiationContext *pInstContext/*=NULL*/) { @@ -3091,11 +3076,11 @@ TypeHandle ClassLoader::LoadTypeHandleForTypeKeyNoLock(TypeKey *pTypeKey, class PendingTypeLoadHolder { Thread * m_pThread; - PendingTypeLoadEntry * m_pEntry; + PendingTypeLoadTable::Entry * m_pEntry; PendingTypeLoadHolder * m_pPrevious; public: - PendingTypeLoadHolder(PendingTypeLoadEntry * pEntry) + PendingTypeLoadHolder(PendingTypeLoadTable::Entry * pEntry) { LIMITED_METHOD_CONTRACT; @@ -3114,7 +3099,7 @@ class PendingTypeLoadHolder m_pThread->SetPendingTypeLoad(m_pPrevious); } - static bool CheckForDeadLockOnCurrentThread(PendingTypeLoadEntry * pEntry) + static bool CheckForDeadLockOnCurrentThread(PendingTypeLoadTable::Entry * pEntry) { LIMITED_METHOD_CONTRACT; @@ -3136,7 +3121,7 @@ class PendingTypeLoadHolder // TypeHandle ClassLoader::LoadTypeHandleForTypeKey_Body( - TypeKey * pTypeKey, + const TypeKey * pTypeKey, TypeHandle typeHnd, ClassLoadLevel targetLevel) { @@ -3167,14 +3152,16 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( #endif } - ReleaseHolder pLoadingEntry; - CrstHolderWithState unresolvedClassLockHolder(&m_UnresolvedClassLock, false); + ReleaseHolder pLoadingEntry; + DWORD dwHashedTypeKey; + PendingTypeLoadTable::Shard *pPendingTypeLoadShard = PendingTypeLoadTable::GetTable()->GetShard(*pTypeKey, this, &dwHashedTypeKey); + CrstHolderWithState unresolvedClassLockHolder(pPendingTypeLoadShard->GetCrst(), false); retry: unresolvedClassLockHolder.Acquire(); // Is it in the hash of classes currently being loaded? - pLoadingEntry = m_pUnresolvedClassHash->GetValue(pTypeKey); + pLoadingEntry = pPendingTypeLoadShard->FindPendingTypeLoadEntry(dwHashedTypeKey, *pTypeKey); if (pLoadingEntry) { pLoadingEntry->AddRef(); @@ -3219,15 +3206,8 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( goto retry; } - { - // Wait for class to be loaded by another thread. This is where we start tracking the - // entry, so there is an implicit Acquire in our use of Assign here. - CrstHolder loadingEntryLockHolder(&pLoadingEntry->m_Crst); - _ASSERTE(pLoadingEntry->HasLock()); - } - // Result of other thread loading the class - HRESULT hr = pLoadingEntry->m_hrResult; + HRESULT hr = pLoadingEntry->DelayForProgress(&typeHnd); if (FAILED(hr)) { @@ -3262,9 +3242,6 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( pLoadingEntry->ThrowException(); } - // Get a pointer to the EEClass being loaded - typeHnd = pLoadingEntry->m_typeHandle; - if (!typeHnd.IsNull()) { // If the type load on the other thread loaded the type to the needed level, return it here. @@ -3294,12 +3271,7 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( // It was not loaded, and it is not being loaded, so we must load it. Create a new LoadingEntry // and acquire it immediately so that other threads will block. - pLoadingEntry = new PendingTypeLoadEntry(*pTypeKey, typeHnd); // this atomically creates a crst and acquires it - - if (!(m_pUnresolvedClassHash->InsertValue(pLoadingEntry))) - { - COMPlusThrowOM(); - } + pLoadingEntry = pPendingTypeLoadShard->InsertPendingTypeLoadEntry(dwHashedTypeKey, *pTypeKey, typeHnd); // this atomically creates a crst and acquires it // Leave the global lock, so that other threads may now start waiting on our class's lock unresolvedClassLockHolder.Release(); @@ -3337,7 +3309,7 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( // Unlink this class from the unresolved class list. unresolvedClassLockHolder.Acquire(); - m_pUnresolvedClassHash->DeleteValue(pTypeKey); + pPendingTypeLoadShard->RemovePendingTypeLoadEntry(pLoadingEntry); // Release the lock before proceeding. The unhandled exception filters take number of locks that // have ordering violations with this lock. @@ -3350,13 +3322,13 @@ ClassLoader::LoadTypeHandleForTypeKey_Body( // Unlink this class from the unresolved class list. unresolvedClassLockHolder.Acquire(); - m_pUnresolvedClassHash->DeleteValue(pTypeKey); + pPendingTypeLoadShard->RemovePendingTypeLoadEntry(pLoadingEntry); unresolvedClassLockHolder.Release(); // Unblock any thread waiting to load same type as in TypeLoadEntry. This should be done - // after pLoadingEntry is removed from m_pUnresolvedClassHash. Otherwise the other thread + // after pLoadingEntry is removed from the PendingTypeLoadTable. Otherwise the other thread // (which was waiting) will keep spinning for a while after waking up, till the current thread removes - // pLoadingEntry from m_pUnresolvedClassHash. This can cause hang in situation when the current + // pLoadingEntry from the PendingTypeLoadTable. This can cause hang in situation when the current // thread is a background thread and so will get very less processor cycle to perform subsequent // operations to remove the entry from hash later. pLoadingEntry->UnblockWaiters(); diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index 0c2cb86cc5e75..251a76a8f78e4 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -30,7 +30,6 @@ class SystemDomain; class Assembly; class ClassLoader; class TypeKey; -class PendingTypeLoadEntry; class PendingTypeLoadTable; class EEClass; class Thread; @@ -466,7 +465,7 @@ void DECLSPEC_NORETURN ThrowTypeAccessException(AccessCheckContext* pContext, // class ClassLoader { - friend class PendingTypeLoadEntry; + friend class PendingTypeLoadTable; friend class MethodTableBuilder; friend class AppDomain; friend class Assembly; @@ -478,10 +477,6 @@ class ClassLoader friend class COMModule; private: - // Classes for which load is in progress - PendingTypeLoadTable * m_pUnresolvedClassHash; - CrstExplicitInit m_UnresolvedClassLock; - // Protects addition of elements to module's m_pAvailableClasses. // (indeed thus protects addition of elements to any m_pAvailableClasses in any // of the modules managed by this loader) @@ -549,7 +544,7 @@ class ClassLoader static Module * ComputeLoaderModule(MethodTable * pMT, mdToken token, // the token of the method Instantiation methodInst); // the type arguments to the method (if any) - static Module * ComputeLoaderModule(TypeKey * typeKey); + static Module * ComputeLoaderModule(const TypeKey * typeKey); inline static PTR_Module ComputeLoaderModuleForFunctionPointer(TypeHandle * pRetAndArgTypes, DWORD NumArgsPlusRetType); inline static PTR_Module ComputeLoaderModuleForParamType(TypeHandle paramType); @@ -798,7 +793,7 @@ class ClassLoader // Load canonical shared instantiation for type key (each instantiation argument is // substituted by CanonicalizeGenericArg) - static TypeHandle LoadCanonicalGenericInstantiation(TypeKey *pTypeKey, + static TypeHandle LoadCanonicalGenericInstantiation(const TypeKey *pTypeKey, LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/); @@ -880,20 +875,20 @@ class ClassLoader friend class AvailableClasses_LockHolder; private: - static TypeHandle LoadConstructedTypeThrowing(TypeKey *pKey, + static TypeHandle LoadConstructedTypeThrowing(const TypeKey *pKey, LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED, const InstantiationContext *pInstContext = NULL); - static TypeHandle LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable); + static TypeHandle LookupTypeKey(const TypeKey *pKey, EETypeHashTable *pTable); - static TypeHandle LookupInLoaderModule(TypeKey* pKey); + static TypeHandle LookupInLoaderModule(const TypeKey* pKey); // Lookup a handle in the appropriate table // (declaring module for TypeDef or loader-module for constructed types) - static TypeHandle LookupTypeHandleForTypeKey(TypeKey *pTypeKey); + static TypeHandle LookupTypeHandleForTypeKey(const TypeKey *pTypeKey); - static void DECLSPEC_NORETURN ThrowTypeLoadException(TypeKey *pKey, UINT resIDWhy); + static void DECLSPEC_NORETURN ThrowTypeLoadException(const TypeKey *pKey, UINT resIDWhy); BOOL IsNested(const NameHandle* pName, mdToken *mdEncloser); @@ -926,16 +921,16 @@ class ClassLoader #ifndef DACCESS_COMPILE // Perform a single phase of class loading // If no type handle has yet been created, typeHnd is null. - static TypeHandle DoIncrementalLoad(TypeKey *pTypeKey, + static TypeHandle DoIncrementalLoad(const TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel workLevel); // Phase CLASS_LOAD_CREATE of class loading - static TypeHandle CreateTypeHandleForTypeKey(TypeKey *pTypeKey, + static TypeHandle CreateTypeHandleForTypeKey(const TypeKey *pTypeKey, AllocMemTracker *pamTracker); // Publish the type in the loader's tables - static TypeHandle PublishType(TypeKey *pTypeKey, TypeHandle typeHnd); + static TypeHandle PublishType(const TypeKey *pTypeKey, TypeHandle typeHnd); // Notify profiler and debugger that a type load has completed // Also update perf counters @@ -957,7 +952,7 @@ class ClassLoader // Create a non-canonical instantiation of a generic type based off the canonical instantiation // (For example, MethodTable for List is based on the MethodTable for List<__Canon>) - static TypeHandle CreateTypeHandleForNonCanonicalGenericInstantiation(TypeKey *pTypeKey, + static TypeHandle CreateTypeHandleForNonCanonicalGenericInstantiation(const TypeKey *pTypeKey, AllocMemTracker *pamTracker); // Loads a class. This is the inner call from the multi-threaded load. This load must @@ -971,12 +966,12 @@ class ClassLoader // The token must be a type def. GC must be enabled. // If we're attempting to load a fresh instantiated type then genericArgs should be filled in - TypeHandle LoadTypeHandleForTypeKey(TypeKey *pTypeKey, + TypeHandle LoadTypeHandleForTypeKey(const TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel level = CLASS_LOADED, const InstantiationContext *pInstContext = NULL); - TypeHandle LoadTypeHandleForTypeKeyNoLock(TypeKey *pTypeKey, + TypeHandle LoadTypeHandleForTypeKeyNoLock(const TypeKey *pTypeKey, ClassLoadLevel level = CLASS_LOADED, const InstantiationContext *pInstContext = NULL); @@ -1020,7 +1015,7 @@ class ClassLoader AllocMemTracker *pamTracker); // don't call this directly. - TypeHandle LoadTypeHandleForTypeKey_Body(TypeKey *pTypeKey, + TypeHandle LoadTypeHandleForTypeKey_Body(const TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel targetLevel); #endif //!DACCESS_COMPILE diff --git a/src/coreclr/vm/crst.h b/src/coreclr/vm/crst.h index 7aacc1a08e028..92438cf1d66a6 100644 --- a/src/coreclr/vm/crst.h +++ b/src/coreclr/vm/crst.h @@ -134,9 +134,9 @@ friend class Crst; friend class DbgTransportLock; #endif // FEATURE_DBGIPC_TRANSPORT_VM - // PendingTypeLoadEntry acquires the lock during construction before anybody has a chance to see it to avoid + // PendingTypeLoadTable::Entry acquires the lock during construction before anybody has a chance to see it to avoid // level violations. - friend class PendingTypeLoadEntry; + friend class PendingTypeLoadTable; public: #ifdef _DEBUG diff --git a/src/coreclr/vm/generics.cpp b/src/coreclr/vm/generics.cpp index 40fc1b3315187..85de1e89dddc2 100644 --- a/src/coreclr/vm/generics.cpp +++ b/src/coreclr/vm/generics.cpp @@ -114,7 +114,7 @@ TypeHandle ClassLoader::CanonicalizeGenericArg(TypeHandle thGenericArg) #ifndef DACCESS_COMPILE -TypeHandle ClassLoader::LoadCanonicalGenericInstantiation(TypeKey *pTypeKey, +TypeHandle ClassLoader::LoadCanonicalGenericInstantiation(const TypeKey *pTypeKey, LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/) { @@ -157,7 +157,7 @@ TypeHandle ClassLoader::LoadCanonicalGenericInstantiation(TypeKey *pTypeKey, /* static */ TypeHandle ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( - TypeKey *pTypeKey, + const TypeKey *pTypeKey, AllocMemTracker *pamTracker) { CONTRACT(TypeHandle) diff --git a/src/coreclr/vm/pendingload.cpp b/src/coreclr/vm/pendingload.cpp index ddfad3fb792a1..6d8923db1f8d4 100644 --- a/src/coreclr/vm/pendingload.cpp +++ b/src/coreclr/vm/pendingload.cpp @@ -12,241 +12,365 @@ #ifndef DACCESS_COMPILE +#ifdef PENDING_TYPE_LOAD_TABLE_STATS +// Enable PENDING_TYPE_LOAD_TABLE_STATS to gather counts of Entry structures which are allocated -// ============================================================================ -// Pending type load hash table methods -// ============================================================================ -/*static */ PendingTypeLoadTable* PendingTypeLoadTable::Create(LoaderHeap *pHeap, - DWORD dwNumBuckets, - AllocMemTracker *pamTracker) +static LONG pendingTypeLoadEntryDynamicAllocations = 0; +void PendingTypeLoadEntryDynamicAlloc() { - CONTRACTL + InterlockedIncrement(&pendingTypeLoadEntryDynamicAllocations); +} +#endif // PENDING_TYPE_LOAD_TABLE_STATS + +PendingTypeLoadTable PendingTypeLoadTable::s_table; + +PendingTypeLoadTable::Entry::Entry() + : m_typeKey(TypeKey::InvalidTypeKey()), + m_fIsPreallocated(true) +{ +} + +PendingTypeLoadTable::Entry::Entry(const TypeKey& typeKey) + : m_typeKey(typeKey), + m_fIsPreallocated(false) +{ +#ifdef PENDING_TYPE_LOAD_TABLE_STATS + PendingTypeLoadEntryDynamicAlloc(); +#endif // PENDING_TYPE_LOAD_TABLE_STATS +} + +void PendingTypeLoadTable::Entry::SetTypeKey(TypeKey typeKey) +{ + m_typeKey = typeKey; +} + +void PendingTypeLoadTable::Entry::InitCrst() +{ + WRAPPER_NO_CONTRACT; + m_Crst.Init(CrstPendingTypeLoadEntry, + CrstFlags(CRST_HOST_BREAKABLE|CRST_UNSAFE_SAMELEVEL)); +} + +void PendingTypeLoadTable::Entry::Init(Entry *pNext, DWORD hash, TypeHandle typeHnd) +{ + WRAPPER_NO_CONTRACT; + + _ASSERTE(m_fIsUnused); + m_dwHash = hash; + m_pNext = pNext; + m_typeHandle = typeHnd; + m_dwWaitCount = 1; + m_hrResult = S_OK; + m_pException = NULL; +#ifdef _DEBUG + if (LoggingOn(LF_CLASSLOADER, LL_INFO10000)) { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pHeap)); + SString name; + TypeString::AppendTypeKeyDebug(name, &m_typeKey); + LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: Creating loading entry for type %s\n", name.GetUTF8())); } - CONTRACTL_END; +#endif - size_t size = sizeof(PendingTypeLoadTable); - BYTE * pMem; - PendingTypeLoadTable * pThis; + m_fIsUnused = false; + m_fLockAcquired = TRUE; + + //--------------------------------------------------------------------------- + // The PendingTypeLoadEntry() lock has a higher level than UnresolvedClassLock. + // But whenever we create one, we have to acquire it while holding the UnresolvedClassLock. + // This is safe since we're the ones that created the lock and are guaranteed to acquire + // it without blocking. But to prevent the crstlevel system from asserting, we + // must acquire using a special method. + //--------------------------------------------------------------------------- + m_Crst.Enter(INDEBUG(Crst::CRST_NO_LEVEL_CHECK)); +} - _ASSERT( dwNumBuckets >= 0 ); - S_SIZE_T allocSize = S_SIZE_T( dwNumBuckets ) - * S_SIZE_T( sizeof(PendingTypeLoadTable::TableEntry*) ) - + S_SIZE_T( size ); - if( allocSize.IsOverflow() ) +void PendingTypeLoadTable::Entry::Reset() +{ + LIMITED_METHOD_CONTRACT; + + if (m_fLockAcquired) { - ThrowHR(E_INVALIDARG); + m_Crst.Leave(); + m_fLockAcquired = false; } - pMem = (BYTE *) pamTracker->Track(pHeap->AllocMem( allocSize )); - pThis = (PendingTypeLoadTable *) pMem; + if (m_pException && !m_pException->IsPreallocatedException()) { + delete m_pException; + m_pException = NULL; + } +} + +bool PendingTypeLoadTable::Entry::IsUnused() +{ + // This VolatileLoad synchrnonizes with the Release() + return (m_fIsUnused && VolatileLoad(&m_fIsUnused)); +} #ifdef _DEBUG - pThis->m_dwDebugMemory = (DWORD)(size + dwNumBuckets*sizeof(PendingTypeLoadTable::TableEntry*)); +bool PendingTypeLoadTable::Entry::HasLock() +{ + LIMITED_METHOD_CONTRACT; + return !!m_Crst.OwnedByCurrentThread(); +} #endif - pThis->m_dwNumBuckets = dwNumBuckets; - pThis->m_pBuckets = (PendingTypeLoadTable::TableEntry**) (pMem + size); +VOID DECLSPEC_NORETURN PendingTypeLoadTable::Entry::ThrowException() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; - // Don't need to memset() since this was ClrVirtualAlloc()'d memory - // memset(pThis->m_pBuckets, 0, dwNumBuckets*sizeof(PendingTypeLoadTable::TableEntry*)); + if (m_pException) + PAL_CPP_THROW(Exception *, m_pException->Clone()); - return pThis; -} + _ASSERTE(FAILED(m_hrResult)); + if (m_hrResult == COR_E_TYPELOAD) + { + ClassLoader::ThrowTypeLoadException(GetTypeKey(), + IDS_CLASSLOAD_GENERAL); + } + else + EX_THROW(EEMessageException, (m_hrResult)); +} -PendingTypeLoadTable::TableEntry *PendingTypeLoadTable::AllocNewEntry() +void PendingTypeLoadTable::Entry::SetException(Exception *pException) { CONTRACTL { NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT( return NULL; ); + PRECONDITION(HasLock()); + PRECONDITION(m_pException == NULL); + PRECONDITION(m_dwWaitCount > 0); } - CONTRACTL_END + CONTRACTL_END; -#ifdef _DEBUG - m_dwDebugMemory += (DWORD) (sizeof(PendingTypeLoadTable::TableEntry)); -#endif + m_typeHandle = TypeHandle(); + m_hrResult = COR_E_TYPELOAD; - return (PendingTypeLoadTable::TableEntry *) new (nothrow) BYTE[sizeof(PendingTypeLoadTable::TableEntry)]; + // we don't care if this fails + // we already know the HRESULT so if we can't store + // the details - so be it + EX_TRY + { + FAULT_NOT_FATAL(); + m_pException = pException->Clone(); + } + EX_CATCH + { + m_pException=NULL; + } + EX_END_CATCH(SwallowAllExceptions); } - -void PendingTypeLoadTable::FreeEntry(PendingTypeLoadTable::TableEntry * pEntry) +void PendingTypeLoadTable::Entry::SetResult(TypeHandle typeHnd) { CONTRACTL { NOTHROW; - GC_NOTRIGGER; - MODE_ANY; + PRECONDITION(HasLock()); + PRECONDITION(m_pException == NULL); + PRECONDITION(m_dwWaitCount > 0); } - CONTRACTL_END - - // keep in sync with the allocator used in AllocNewEntry - delete[] ((BYTE*)pEntry); + CONTRACTL_END; -#ifdef _DEBUG - m_dwDebugMemory -= (DWORD) (sizeof(PendingTypeLoadTable::TableEntry)); -#endif + m_typeHandle = typeHnd; } - -// -// Does not handle duplicates! -// -BOOL PendingTypeLoadTable::InsertValue(PendingTypeLoadEntry *pData) +void PendingTypeLoadTable::Entry::UnblockWaiters() { CONTRACTL { NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT( return FALSE; ); - PRECONDITION(CheckPointer(pData)); - PRECONDITION(FindItem(&pData->GetTypeKey()) == NULL); + PRECONDITION(HasLock()); + PRECONDITION(m_dwWaitCount > 0); } - CONTRACTL_END - - _ASSERTE(m_dwNumBuckets != 0); - - DWORD dwHash = HashTypeKey(&pData->GetTypeKey()); - DWORD dwBucket = dwHash % m_dwNumBuckets; - PendingTypeLoadTable::TableEntry * pNewEntry = AllocNewEntry(); - if (pNewEntry == NULL) - return FALSE; + CONTRACTL_END; - // Insert at head of bucket - pNewEntry->pNext = m_pBuckets[dwBucket]; - pNewEntry->pData = pData; - pNewEntry->dwHashValue = dwHash; + _ASSERTE(m_fLockAcquired); + m_Crst.Leave(); + m_fLockAcquired = FALSE; +} - m_pBuckets[dwBucket] = pNewEntry; +const TypeKey* PendingTypeLoadTable::Entry::GetTypeKey() +{ + LIMITED_METHOD_CONTRACT; + return &m_typeKey; +} - return TRUE; +void PendingTypeLoadTable::Entry::AddRef() +{ + LIMITED_METHOD_CONTRACT; + InterlockedIncrement(&m_dwWaitCount); } -BOOL PendingTypeLoadTable::DeleteValue(TypeKey *pKey) +void PendingTypeLoadTable::Entry::Release() { - CONTRACTL + LIMITED_METHOD_CONTRACT; + if (InterlockedDecrement(&m_dwWaitCount) == 0) { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - FORBID_FAULT; - PRECONDITION(CheckPointer(pKey)); - } - CONTRACTL_END + Reset(); - _ASSERTE(m_dwNumBuckets != 0); + if (this->m_fIsPreallocated) + { + // We won't be holding the lock while Releasing, so use a VolatileStore to ensure all writes during Reset are complete. + VolatileStore(&m_fIsUnused, true); + } + else + { + // Call the derived type with a destructor + delete static_cast(this); + } + } +} - DWORD dwHash = HashTypeKey(pKey); - DWORD dwBucket = dwHash % m_dwNumBuckets; - PendingTypeLoadTable::TableEntry * pSearch; - PendingTypeLoadTable::TableEntry **ppPrev = &m_pBuckets[dwBucket]; +bool PendingTypeLoadTable::Entry::HasWaiters() +{ + LIMITED_METHOD_CONTRACT; + return m_dwWaitCount > 1; +} - for (pSearch = m_pBuckets[dwBucket]; pSearch; pSearch = pSearch->pNext) +HRESULT PendingTypeLoadTable::Entry::DelayForProgress(TypeHandle* typeHndWithProgress) +{ + STANDARD_VM_CONTRACT; + HRESULT hr = S_OK; { - TypeKey entryTypeKey = pSearch->pData->GetTypeKey(); - if (pSearch->dwHashValue == dwHash && TypeKey::Equals(pKey, &entryTypeKey)) + CrstHolder crstHolder(&m_Crst); + _ASSERTE(HasLock()); + hr = m_hrResult; + + if (SUCCEEDED(hr)) { - *ppPrev = pSearch->pNext; - FreeEntry(pSearch); - return TRUE; + *typeHndWithProgress = m_typeHandle; } - - ppPrev = &pSearch->pNext; } - return FALSE; + return hr; } +void PendingTypeLoadTable::Shard::Init() +{ + m_shardCrst.Init(CrstUnresolvedClassLock); + for (int i = 0; i < PreallocatedEntryCount; i++) + { + m_preAllocatedEntries[i].InitCrst(); + } +} -PendingTypeLoadTable::TableEntry *PendingTypeLoadTable::FindItem(TypeKey *pKey) +PendingTypeLoadTable::Entry* PendingTypeLoadTable::Shard::FindPendingTypeLoadEntry(DWORD hash, const TypeKey& typeKey) { - CONTRACTL + WRAPPER_NO_CONTRACT; + for (PendingTypeLoadTable::Entry *current = m_pLinkedListOfActiveEntries; current != NULL; current = current->m_pNext) { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - FORBID_FAULT; - PRECONDITION(CheckPointer(pKey)); + if (current->m_dwHash != hash) + continue; + if (TypeKey::Equals(&typeKey, current->GetTypeKey())) + { + return current; + } } - CONTRACTL_END - _ASSERTE(m_dwNumBuckets != 0); + return NULL; +} +void PendingTypeLoadTable::Shard::RemovePendingTypeLoadEntry(Entry* pEntry) +{ + LIMITED_METHOD_CONTRACT; - DWORD dwHash = HashTypeKey(pKey); - DWORD dwBucket = dwHash % m_dwNumBuckets; - PendingTypeLoadTable::TableEntry * pSearch; + Entry **pCurrent = &m_pLinkedListOfActiveEntries; - for (pSearch = m_pBuckets[dwBucket]; pSearch; pSearch = pSearch->pNext) + while (*pCurrent != pEntry) { - TypeKey entryTypeKey = pSearch->pData->GetTypeKey(); - if (pSearch->dwHashValue == dwHash && TypeKey::Equals(pKey, &entryTypeKey)) + pCurrent = &((*pCurrent)->m_pNext); + } + *pCurrent = (*pCurrent)->m_pNext; +} + +PendingTypeLoadTable::Entry* PendingTypeLoadTable::Shard::InsertPendingTypeLoadEntry(DWORD hash, const TypeKey& typeKey, TypeHandle typeHnd) +{ + STANDARD_VM_CONTRACT; + Entry* result = NULL; + + for (int iEntry = 0; iEntry < PreallocatedEntryCount; iEntry++) + { + if (m_preAllocatedEntries[iEntry].IsUnused()) { - return pSearch; + result = &m_preAllocatedEntries[iEntry]; + result->SetTypeKey(typeKey); + result->Init(m_pLinkedListOfActiveEntries, hash, typeHnd); + break; } } + if (result == NULL) + { + NewHolder dynamicResult(new DynamicallyAllocatedEntry(typeKey)); + dynamicResult->Init(m_pLinkedListOfActiveEntries, hash, typeHnd); + result = dynamicResult.Extract(); + } + + m_pLinkedListOfActiveEntries = result; + return result; +} - return NULL; +/*static*/ +PendingTypeLoadTable* PendingTypeLoadTable::GetTable() +{ + LIMITED_METHOD_CONTRACT; + return &s_table; } +/*static*/ +void PendingTypeLoadTable::Init() +{ + STANDARD_VM_CONTRACT; + for (int i = 0; i < PendingTypeLoadTableShardCount; i++) + GetTable()->m_shards[i].Init(); +} + +PendingTypeLoadTable::Shard* PendingTypeLoadTable::GetShard(const TypeKey &typeKey, ClassLoader* pClassLoader, DWORD *pHashCodeForType) +{ + STANDARD_VM_CONTRACT; + DWORD hash = HashTypeKey(&typeKey) ^ (DWORD)(size_t)pClassLoader; // Mix in some entropy about which classloader is in use + *pHashCodeForType = hash; + return &m_shards[hash % PendingTypeLoadTableShardCount]; +} #ifdef _DEBUG void PendingTypeLoadTable::Dump() { - CONTRACTL + STANDARD_VM_CONTRACT; + for (int iShard = 0; iShard < PendingTypeLoadTableShardCount; iShard++) { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END - - LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: table contains:\n")); - for (DWORD i = 0; i < m_dwNumBuckets; i++) - { - for (TableEntry *pSearch = m_pBuckets[i]; pSearch; pSearch = pSearch->pNext) - { - SString name; - TypeKey entryTypeKey = pSearch->pData->GetTypeKey(); - TypeString::AppendTypeKeyDebug(name, &entryTypeKey); - LOG((LF_CLASSLOADER, LL_INFO10000, " Entry %s with handle %p at level %s\n", name.GetUTF8(), pSearch->pData->m_typeHandle.AsPtr(), - pSearch->pData->m_typeHandle.IsNull() ? "not-applicable" : classLoadLevelName[pSearch->pData->m_typeHandle.GetLoadLevel()])); - } + CrstHolder unresolvedClassLockHolder(m_shards[iShard].GetCrst()); + m_shards[iShard].Dump(); } } -#endif -PendingTypeLoadEntry* PendingTypeLoadTable::GetValue(TypeKey *pKey) +void PendingTypeLoadTable::Shard::Dump() { CONTRACTL { - NOTHROW; - GC_NOTRIGGER; + THROWS; + GC_TRIGGERS; MODE_ANY; - FORBID_FAULT; - PRECONDITION(CheckPointer(pKey)); } CONTRACTL_END - PendingTypeLoadTable::TableEntry *pItem = FindItem(pKey); - - if (pItem != NULL) + LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: shard contains:\n")); + for (Entry *pSearch = this->m_pLinkedListOfActiveEntries; pSearch; pSearch = pSearch->m_pNext) { - return pItem->pData; - } - else - { - return NULL; + SString name; + TypeString::AppendTypeKeyDebug(name, pSearch->GetTypeKey()); + LOG((LF_CLASSLOADER, LL_INFO10000, " Entry %s with handle %p at level %s\n", name.GetUTF8(), pSearch->m_typeHandle.AsPtr(), + pSearch->m_typeHandle.IsNull() ? "not-applicable" : classLoadLevelName[pSearch->m_typeHandle.GetLoadLevel()])); } } +#endif #endif // #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/pendingload.h b/src/coreclr/vm/pendingload.h index c9619c082211b..ec939fe7697ec 100644 --- a/src/coreclr/vm/pendingload.h +++ b/src/coreclr/vm/pendingload.h @@ -20,245 +20,149 @@ // // A temporary structure used when loading and resolving classes // -class PendingTypeLoadEntry -{ - friend class ClassLoader; // workaround really need to beef up the API below +// Hash table used to hold pending type loads +// This is a sharded fixed bucket count hashtable, with locking per shard +// This is a singleton +class PendingTypeLoadTable +{ public: - PendingTypeLoadEntry(TypeKey typeKey, TypeHandle typeHnd) - : m_Crst( - CrstPendingTypeLoadEntry, - CrstFlags(CRST_HOST_BREAKABLE|CRST_UNSAFE_SAMELEVEL) - ), - m_typeKey(typeKey) - + class Entry { - WRAPPER_NO_CONTRACT; - - m_typeHandle = typeHnd; - m_dwWaitCount = 1; - m_hrResult = S_OK; - m_pException = NULL; -#ifdef _DEBUG - if (LoggingOn(LF_CLASSLOADER, LL_INFO10000)) - { - SString name; - TypeString::AppendTypeKeyDebug(name, &m_typeKey); - LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: Creating loading entry for type %s\n", name.GetUTF8())); - } -#endif - - m_fLockAcquired = TRUE; - - //--------------------------------------------------------------------------- - // The PendingTypeLoadEntry() lock has a higher level than UnresolvedClassLock. - // But whenever we create one, we have to acquire it while holding the UnresolvedClassLock. - // This is safe since we're the ones that created the lock and are guaranteed to acquire - // it without blocking. But to prevent the crstlevel system from asserting, we - // must acquire using a special method. - //--------------------------------------------------------------------------- - m_Crst.Enter(INDEBUG(Crst::CRST_NO_LEVEL_CHECK)); - } - - ~PendingTypeLoadEntry() - { - LIMITED_METHOD_CONTRACT; - - if (m_fLockAcquired) - m_Crst.Leave(); - - if (m_pException && !m_pException->IsPreallocatedException()) { - delete m_pException; - } - } - -#ifdef _DEBUG - BOOL HasLock() - { - LIMITED_METHOD_CONTRACT; - return m_Crst.OwnedByCurrentThread(); - } -#endif - -#ifndef DACCESS_COMPILE - VOID DECLSPEC_NORETURN ThrowException() - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - if (m_pException) - PAL_CPP_THROW(Exception *, m_pException->Clone()); - - _ASSERTE(FAILED(m_hrResult)); - - if (m_hrResult == COR_E_TYPELOAD) - { - TypeKey typeKey = GetTypeKey(); - ClassLoader::ThrowTypeLoadException(&typeKey, - IDS_CLASSLOAD_GENERAL); - - } - else - EX_THROW(EEMessageException, (m_hrResult)); - } + friend class PendingTypeLoadTable; + private: + Entry(); + Entry(const TypeKey& typeKey); + + protected: + + void SetTypeKey(TypeKey typeKey); + void InitCrst(); + void Init(Entry *pNext, DWORD hash, TypeHandle typeHnd); + void Reset(); + bool IsUnused(); + + public: + + #ifdef _DEBUG + bool HasLock(); + #endif + + VOID DECLSPEC_NORETURN ThrowException(); + void SetException(Exception *pException); + void SetResult(TypeHandle typeHnd); + void UnblockWaiters(); + const TypeKey* GetTypeKey(); + + void AddRef(); + void Release(); + bool HasWaiters(); + // Call this when After calling AddRef to see what the next amount of progress to wait for the load in progress to complete + // and to find the TypeHandle if progress was successful. + HRESULT DelayForProgress(TypeHandle* typeHndWithProgress); + + protected: + Entry* m_pNext; + CrstStatic m_Crst; // While this isn't a static, we use CrstStatic, so that Entry structs can be part of a static variable + + public: + // Result of loading; this is first created in the CREATE stage of class loading + TypeHandle m_typeHandle; + + private: + // Type that we're loading + TypeKey m_typeKey; + + // Number of threads waiting for this type + LONG m_dwWaitCount; + + // Error result, propagated to all threads loading this class + HRESULT m_hrResult; + + // Exception object to throw + Exception *m_pException; + DWORD m_dwHash; + + // m_Crst was acquired + bool m_fLockAcquired; + bool m_fIsPreallocated; + bool m_fIsUnused = true; + }; - void SetException(Exception *pException) +private: + class DynamicallyAllocatedEntry : public Entry { - CONTRACTL + public: + DynamicallyAllocatedEntry(const TypeKey& typeKey) : Entry(typeKey) { - NOTHROW; - PRECONDITION(HasLock()); - PRECONDITION(m_pException == NULL); - PRECONDITION(m_dwWaitCount > 0); + InitCrst(); } - CONTRACTL_END; - m_typeHandle = TypeHandle(); - m_hrResult = COR_E_TYPELOAD; - - // we don't care if this fails - // we already know the HRESULT so if we can't store - // the details - so be it - EX_TRY - { - FAULT_NOT_FATAL(); - m_pException = pException->Clone(); - } - EX_CATCH + void Init(Entry *pNext, DWORD hash, TypeHandle typeHnd) { - m_pException=NULL; + WRAPPER_NO_CONTRACT; + Entry::Init(pNext, hash, typeHnd); } - EX_END_CATCH(SwallowAllExceptions); - } - void SetResult(TypeHandle typeHnd) - { - CONTRACTL + ~DynamicallyAllocatedEntry() { - NOTHROW; - PRECONDITION(HasLock()); - PRECONDITION(m_pException == NULL); - PRECONDITION(m_dwWaitCount > 0); + WRAPPER_NO_CONTRACT; + m_Crst.Destroy(); } - CONTRACTL_END; - - m_typeHandle = typeHnd; - } - - void UnblockWaiters() - { - CONTRACTL - { - NOTHROW; - PRECONDITION(HasLock()); - PRECONDITION(m_dwWaitCount > 0); - } - CONTRACTL_END; - - _ASSERTE(m_fLockAcquired); - m_Crst.Leave(); - m_fLockAcquired = FALSE; - } -#endif //DACCESS_COMPILE - - TypeKey& GetTypeKey() - { - LIMITED_METHOD_CONTRACT; - return m_typeKey; - } - - void AddRef() - { - LIMITED_METHOD_CONTRACT; - InterlockedIncrement(&m_dwWaitCount); - } + }; - void Release() + class StaticallyAllocatedEntry : public Entry { - LIMITED_METHOD_CONTRACT; - if (InterlockedDecrement(&m_dwWaitCount) == 0) - delete this; - } + }; - BOOL HasWaiters() +public: + struct Shard { - LIMITED_METHOD_CONTRACT; - return m_dwWaitCount > 1; - } - - private: - Crst m_Crst; - - public: - // Result of loading; this is first created in the CREATE stage of class loading - TypeHandle m_typeHandle; - - private: - // Type that we're loading - TypeKey m_typeKey; - - // Number of threads waiting for this type - LONG m_dwWaitCount; - - // Error result, propagated to all threads loading this class - HRESULT m_hrResult; + friend class PendingTypeLoadTable; + // This number chosen by experimentation with a fairly complex ASP.NET application that would naturally use about 40,000 Entry structures on startup. + // Entry allocations were shifted to about 11 during that startup phase. + static constexpr int PreallocatedEntryCount = 2; - // Exception object to throw - Exception *m_pException; - - // m_Crst was acquired - BOOL m_fLockAcquired; -}; - -// Hash table used to hold pending type loads -// @todo : use shash.h when it supports LoaderHeap/Alloc\MemTracker -class PendingTypeLoadTable -{ -protected: - struct TableEntry - { - TableEntry* pNext; - DWORD dwHashValue; - PendingTypeLoadEntry* pData; - }; +private: + Shard() = default; - TableEntry **m_pBuckets; // Pointer to first entry for each bucket - DWORD m_dwNumBuckets; + Entry *m_pLinkedListOfActiveEntries = NULL; + CrstStatic m_shardCrst; + Entry m_preAllocatedEntries[PreallocatedEntryCount]; + void Init(); public: + CrstBase* GetCrst() + { + LIMITED_METHOD_CONTRACT; + return &m_shardCrst; + } + + Entry* FindPendingTypeLoadEntry(DWORD hash, const TypeKey& typeKey); + void RemovePendingTypeLoadEntry(Entry* pEntry); + Entry* InsertPendingTypeLoadEntry(DWORD hash, const TypeKey& typeKey, TypeHandle typeHnd); #ifdef _DEBUG - DWORD m_dwDebugMemory; + void Dump(); #endif + }; - static PendingTypeLoadTable *Create(LoaderHeap *pHeap, DWORD dwNumBuckets, AllocMemTracker *pamTracker); + // This number chosen by experimentation with a fairly complex ASP.NET application that would naturally allocate + // about 40,000 Entry structures on startup. With PendingTypeLoadTableShardCount(31) number of shards and + // PreallocatedEntryCount(2) number of pre-allocated entries in each shard, the number of dynamic allocations of + // Entry structures was reduced to 11. + static constexpr int PendingTypeLoadTableShardCount = 31; + Shard m_shards[PendingTypeLoadTableShardCount]; -private: - // These functions don't actually exist - declared private to prevent bypassing PendingTypeLoadTable::Create - void * operator new(size_t size); - void operator delete(void *p); - - PendingTypeLoadTable(); - ~PendingTypeLoadTable(); + static PendingTypeLoadTable s_table; + static PendingTypeLoadTable* GetTable(); public: - BOOL InsertValue(PendingTypeLoadEntry* pEntry); - BOOL DeleteValue(TypeKey *pKey); - PendingTypeLoadEntry* GetValue(TypeKey *pKey); - TableEntry* AllocNewEntry(); - void FreeEntry(TableEntry* pEntry); + static void Init(); + Shard* GetShard(const TypeKey &typeKey, ClassLoader* pClassLoader, DWORD *pHashCodeForType); #ifdef _DEBUG - void Dump(); + void Dump(); #endif - -private: - TableEntry* FindItem(TypeKey *pKey); }; - #endif // _H_PENDINGLOAD diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index 053cc759217c7..48855c8b3ec3b 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -1516,7 +1516,7 @@ TypeKey TypeHandle::GetTypeKey() const #ifdef _DEBUG // Check that a type handle matches the key provided -CHECK TypeHandle::CheckMatchesKey(TypeKey *pKey) const +CHECK TypeHandle::CheckMatchesKey(const TypeKey *pKey) const { WRAPPER_NO_CONTRACT; CONTRACT_VIOLATION(TakesLockViolation); // this is debug-only code diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index 8e2287eb9d3f2..d0497ee5310a4 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -385,7 +385,7 @@ class TypeHandle #ifdef _DEBUG // Check that this type matches the key given // i.e. that all aspects (element type, module/token, rank for arrays, instantiation for generic types) match up - CHECK CheckMatchesKey(TypeKey *pKey) const; + CHECK CheckMatchesKey(const TypeKey *pKey) const; // Check that this type is loaded up to the level indicated // Also check that it is non-null diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index dfc355c3fafba..b2684f858767e 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -239,7 +239,7 @@ static DWORD HashTypeHandle(TypeHandle t) } // Calculate hash value from key -DWORD HashTypeKey(TypeKey* pKey) +DWORD HashTypeKey(const TypeKey* pKey) { CONTRACTL { @@ -275,7 +275,7 @@ DWORD HashTypeKey(TypeKey* pKey) // We avoid restoring types during search by cracking the signature // encoding used by the zapper for out-of-module types e.g. in the // instantiation of an instantiated type. -EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) +EETypeHashEntry_t *EETypeHashTable::FindItem(const TypeKey* pKey) { CONTRACTL { @@ -478,7 +478,7 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg #endif // #ifndef DACCESS_COMPILE } -TypeHandle EETypeHashTable::GetValue(TypeKey *pKey) +TypeHandle EETypeHashTable::GetValue(const TypeKey *pKey) { CONTRACTL { diff --git a/src/coreclr/vm/typehash.h b/src/coreclr/vm/typehash.h index b706ee64a4493..8afe7dff89a3b 100644 --- a/src/coreclr/vm/typehash.h +++ b/src/coreclr/vm/typehash.h @@ -26,7 +26,7 @@ // //======================================================================================== -DWORD HashTypeKey(TypeKey* pKey); +DWORD HashTypeKey(const TypeKey* pKey); // One of these is present for each element in the table // It simply chains together (hash,data) pairs @@ -100,7 +100,7 @@ class EETypeHashTable : public DacEnumerableHashTable