Replace multi-loaderallocator hash implementation in MethodDescBackpatchInfo (#22285)
authorDavid Wrighton <davidwr@microsoft.com>
Fri, 15 Feb 2019 01:07:14 +0000 (17:07 -0800)
committerGitHub <noreply@github.com>
Fri, 15 Feb 2019 01:07:14 +0000 (17:07 -0800)
* GCHeapHash
- Hashtable implementation for runtime use
- Implementation written in C++
- Data storage in managed heap memory
- Based on SHash design, but using managed memory

CrossLoaderAllocatorHash
- Hash for c++ Pointer to C++ pointer where the lifetimes are controlled by different loader allocators
 - Support for add/remove/visit all entries of 1 key/visit all entries/ remove all entries of 1 key
- Supports holding data which is unmanaged, but data items themselves can be of any size (key/value are templated types)

* Swap MethodDescBackpatchInfo to use the CrossLoaderAllocatorHash

* The MethodDescBackpatchCrst needs to be around an allocation
- Adjust the Crst so that it can safely be used around code which allocates
- Required moving its use out from within the EESuspend logic used in rejit

23 files changed:
src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs [new file with mode: 0644]
src/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs [new file with mode: 0644]
src/inc/CrstTypes.def
src/inc/crsttypes.h
src/vm/appdomain.cpp
src/vm/codeversion.cpp
src/vm/crossloaderallocatorhash.h [new file with mode: 0644]
src/vm/crossloaderallocatorhash.inl [new file with mode: 0644]
src/vm/frames.h
src/vm/gcheaphashtable.h [new file with mode: 0644]
src/vm/gcheaphashtable.inl [new file with mode: 0644]
src/vm/loaderallocator.cpp
src/vm/loaderallocator.hpp
src/vm/method.cpp
src/vm/methoddescbackpatchinfo.cpp
src/vm/methoddescbackpatchinfo.h
src/vm/mscorlib.h
src/vm/object.cpp
src/vm/object.h
src/vm/prestub.cpp
src/vm/rejit.cpp
src/vm/tieredcompilation.cpp

index 73fc430..4a08a06 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CrossLoaderAllocatorHashHelpers.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GCHeapHash.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\jithelpers.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs
new file mode 100644 (file)
index 0000000..299fb60
--- /dev/null
@@ -0,0 +1,37 @@
+// 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.Runtime.InteropServices;
+
+namespace System.Runtime.CompilerServices
+{
+    /// <summary>
+    /// Managed structure used by CrossLoaderAllocatorHeap to isolate per LoaderAllocator
+    /// data. 
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal class LAHashDependentHashTracker
+    {
+        GCHandle _dependentHandle;
+        IntPtr _loaderAllocator;
+
+        ~LAHashDependentHashTracker()
+        {
+            if (_dependentHandle.IsAllocated)
+                _dependentHandle.Free();
+        }
+    }
+
+    /// <summary>
+    /// Managed structure used by CrossLoaderAllocatorHeap to hold a set of references
+    /// to LAHashDependentHashTracker's
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal class LAHashKeyToTrackers
+    {
+        object _trackerOrTrackerSet;
+        object _laLocalKeyValueStore;
+    }
+}
\ No newline at end of file
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs
new file mode 100644 (file)
index 0000000..3699585
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.Runtime.InteropServices;
+
+namespace System.Runtime.CompilerServices
+{
+    /// <summary>
+    /// Managed structure used by GCHeapHash in CLR to provide a hashtable manipulated
+    /// by C++ runtime code which manages its memory in the GC heap.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal class GCHeapHash
+    {
+        Array _data;
+        int _count;
+        int _deletedCount;
+    }
+}
\ No newline at end of file
index 5900a33..b6862c1 100644 (file)
@@ -491,8 +491,8 @@ End
 
 // Used to synchronize all rejit information stored in a given AppDomain.
 Crst ReJITDomainTable
-    AcquiredBefore LoaderHeap SingleUseLock DeadlockDetection JumpStubCache DebuggerController MethodDescBackpatchInfoTracker
-    AcquiredAfter ReJITGlobalRequest ThreadStore GlobalStrLiteralMap SystemDomain DebuggerMutex
+    AcquiredBefore LoaderHeap SingleUseLock DeadlockDetection JumpStubCache DebuggerController FuncPtrStubs
+    AcquiredAfter ReJITGlobalRequest ThreadStore GlobalStrLiteralMap SystemDomain DebuggerMutex MethodDescBackpatchInfoTracker
 End
 
 // Used to synchronize all global requests (which may span multiple AppDomains) which add
@@ -701,5 +701,5 @@ Crst COMCallWrapper
 End
 
 Crst MethodDescBackpatchInfoTracker
-    AcquiredBefore FuncPtrStubs
+    AcquiredBefore FuncPtrStubs ThreadStore SystemDomain
 End
index e8413dd..0e6b497 100644 (file)
@@ -179,158 +179,158 @@ enum CrstType
 // An array mapping CrstType to level.
 int g_rgCrstLevelMap[] =
 {
-    9,                 // CrstAllowedFiles
-    9,                 // CrstAppDomainCache
-    14,                        // CrstAppDomainHandleTable
-    0,                 // CrstArgBasedStubCache
-    0,                 // CrstAssemblyDependencyGraph
-    0,                 // CrstAssemblyIdentityCache
-    0,                 // CrstAssemblyList
-    7,                 // CrstAssemblyLoader
-    3,                 // CrstAvailableClass
-    3,                 // CrstAvailableParamTypes
-    7,                 // CrstBaseDomain
-    -1,                        // CrstCCompRC
-    9,                 // CrstCer
-    13,                        // CrstClassFactInfoHash
-    8,                 // CrstClassInit
-    -1,                        // CrstClrNotification
-    0,                 // CrstCLRPrivBinderMaps
-    3,                 // CrstCLRPrivBinderMapsAdd
-    6,                 // CrstCodeFragmentHeap
-    0,                 // CrstCOMCallWrapper
-    4,                 // CrstCOMWrapperCache
-    0,                 // CrstConnectionNameTable
-    17,                        // CrstContexts
-    -1,                        // CrstCoreCLRBinderLog
-    0,                 // CrstCrstCLRPrivBinderLocalWinMDPath
-    7,                 // CrstCSPCache
-    3,                 // CrstDataTest1
-    0,                 // CrstDataTest2
-    0,                 // CrstDbgTransport
-    0,                 // CrstDeadlockDetection
-    -1,                        // CrstDebuggerController
-    3,                 // CrstDebuggerFavorLock
-    0,                 // CrstDebuggerHeapExecMemLock
-    0,                 // CrstDebuggerHeapLock
-    4,                 // CrstDebuggerJitInfo
-    11,                        // CrstDebuggerMutex
-    0,                 // CrstDelegateToFPtrHash
-    16,                        // CrstDomainLocalBlock
-    0,                 // CrstDynamicIL
-    3,                 // CrstDynamicMT
-    3,                 // CrstDynLinkZapItems
-    7,                 // CrstEtwTypeLogHash
-    19,                        // CrstEventPipe
-    0,                 // CrstEventStore
-    0,                 // CrstException
-    7,                 // CrstExecuteManLock
-    0,                 // CrstExecuteManRangeLock
-    3,                 // CrstFCall
-    7,                 // CrstFriendAccessCache
-    7,                 // CrstFuncPtrStubs
-    5,                 // CrstFusionAppCtx
-    11,                        // CrstGCCover
-    0,                 // CrstGCMemoryPressure
-    13,                        // CrstGlobalStrLiteralMap
-    1,                 // CrstHandleTable
-    0,                 // CrstHostAssemblyMap
-    3,                 // CrstHostAssemblyMapAdd
-    0,                 // CrstIbcProfile
-    9,                 // CrstIJWFixupData
-    0,                 // CrstIJWHash
-    7,                 // CrstILStubGen
-    3,                 // CrstInlineTrackingMap
-    17,                        // CrstInstMethodHashTable
-    0,                 // CrstInterfaceVTableMap
-    19,                        // CrstInterop
-    4,                 // CrstInteropData
-    13,                        // CrstIOThreadpoolWorker
-    0,                 // CrstIsJMCMethod
-    7,                 // CrstISymUnmanagedReader
-    8,                 // CrstJit
-    0,                 // CrstJitGenericHandleCache
-    -1,                        // CrstJitPerf
-    6,                 // CrstJumpStubCache
-    0,                 // CrstLeafLock
-    -1,                        // CrstListLock
-    15,                        // CrstLoaderAllocator
-    16,                        // CrstLoaderAllocatorReferences
-    0,                 // CrstLoaderHeap
-    0,                 // CrstMda
-    -1,                        // CrstMetadataTracker
-    9,                 // CrstMethodDescBackpatchInfoTracker
-    0,                 // CrstModIntPairList
-    4,                 // CrstModule
-    15,                        // CrstModuleFixup
-    3,                 // CrstModuleLookupTable
-    0,                 // CrstMulticoreJitHash
-    13,                        // CrstMulticoreJitManager
-    0,                 // CrstMUThunkHash
-    -1,                        // CrstNativeBinderInit
-    -1,                        // CrstNativeImageCache
-    0,                 // CrstNls
-    0,                 // CrstNotifyGdb
-    2,                 // CrstObjectList
-    0,                 // CrstOnEventManager
-    0,                 // CrstPatchEntryPoint
-    4,                 // CrstPEImage
-    0,                 // CrstPEImagePDBStream
-    18,                        // CrstPendingTypeLoadEntry
-    0,                 // CrstPinHandle
-    0,                 // CrstPinnedByrefValidation
-    0,                 // CrstProfilerGCRefDataFreeList
-    0,                 // CrstProfilingAPIStatus
-    0,                 // CrstPublisherCertificate
-    3,                 // CrstRCWCache
-    0,                 // CrstRCWCleanupList
-    3,                 // CrstRCWRefCache
-    4,                 // CrstReadyToRunEntryPointToMethodDescMap
-    0,                 // CrstReDacl
-    9,                 // CrstReflection
-    10,                        // CrstReJITDomainTable
-    14,                        // CrstReJITGlobalRequest
-    19,                        // CrstRemoting
-    3,                 // CrstRetThunkCache
-    0,                 // CrstRWLock
-    3,                 // CrstSavedExceptionInfo
-    0,                 // CrstSaveModuleProfileData
-    0,                 // CrstSecurityStackwalkCache
-    4,                 // CrstSharedAssemblyCreate
-    3,                 // CrstSigConvert
-    5,                 // CrstSingleUseLock
-    0,                 // CrstSpecialStatics
-    0,                 // CrstSqmManager
-    0,                 // CrstStackSampler
-    -1,                        // CrstStressLog
-    0,                 // CrstStrongName
-    5,                 // CrstStubCache
-    0,                 // CrstStubDispatchCache
-    4,                 // CrstStubUnwindInfoHeapSegments
-    3,                 // CrstSyncBlockCache
-    0,                 // CrstSyncHashLock
-    4,                 // CrstSystemBaseDomain
-    13,                        // CrstSystemDomain
-    0,                 // CrstSystemDomainDelayedUnloadList
-    0,                 // CrstThreadIdDispenser
-    0,                 // CrstThreadpoolEventCache
-    7,                 // CrstThreadpoolTimerQueue
-    7,                 // CrstThreadpoolWaitThreads
-    13,                        // CrstThreadpoolWorker
-    4,                 // CrstThreadStaticDataHashTable
-    12,                        // CrstThreadStore
-    9,                 // CrstTieredCompilation
-    9,                 // CrstTPMethodTable
-    3,                 // CrstTypeEquivalenceMap
-    7,                 // CrstTypeIDMap
-    3,                 // CrstUMEntryThunkCache
-    0,                 // CrstUMThunkHash
-    3,                 // CrstUniqueStack
-    7,                 // CrstUnresolvedClassLock
-    3,                 // CrstUnwindInfoTableLock
-    3,                 // CrstVSDIndirectionCellLock
-    3,                 // CrstWinRTFactoryCache
-    3,                 // CrstWrapperTemplate
+    9,          // CrstAllowedFiles
+    9,          // CrstAppDomainCache
+    13,         // CrstAppDomainHandleTable
+    0,          // CrstArgBasedStubCache
+    0,          // CrstAssemblyDependencyGraph
+    0,          // CrstAssemblyIdentityCache
+    0,          // CrstAssemblyList
+    7,          // CrstAssemblyLoader
+    3,          // CrstAvailableClass
+    3,          // CrstAvailableParamTypes
+    7,          // CrstBaseDomain
+    -1,         // CrstCCompRC
+    9,          // CrstCer
+    12,         // CrstClassFactInfoHash
+    8,          // CrstClassInit
+    -1,         // CrstClrNotification
+    0,          // CrstCLRPrivBinderMaps
+    3,          // CrstCLRPrivBinderMapsAdd
+    6,          // CrstCodeFragmentHeap
+    0,          // CrstCOMCallWrapper
+    4,          // CrstCOMWrapperCache
+    0,          // CrstConnectionNameTable
+    16,         // CrstContexts
+    -1,         // CrstCoreCLRBinderLog
+    0,          // CrstCrstCLRPrivBinderLocalWinMDPath
+    7,          // CrstCSPCache
+    3,          // CrstDataTest1
+    0,          // CrstDataTest2
+    0,          // CrstDbgTransport
+    0,          // CrstDeadlockDetection
+    -1,         // CrstDebuggerController
+    3,          // CrstDebuggerFavorLock
+    0,          // CrstDebuggerHeapExecMemLock
+    0,          // CrstDebuggerHeapLock
+    4,          // CrstDebuggerJitInfo
+    10,         // CrstDebuggerMutex
+    0,          // CrstDelegateToFPtrHash
+    15,         // CrstDomainLocalBlock
+    0,          // CrstDynamicIL
+    3,          // CrstDynamicMT
+    3,          // CrstDynLinkZapItems
+    7,          // CrstEtwTypeLogHash
+    18,         // CrstEventPipe
+    0,          // CrstEventStore
+    0,          // CrstException
+    7,          // CrstExecuteManLock
+    0,          // CrstExecuteManRangeLock
+    3,          // CrstFCall
+    7,          // CrstFriendAccessCache
+    7,          // CrstFuncPtrStubs
+    5,          // CrstFusionAppCtx
+    10,         // CrstGCCover
+    0,          // CrstGCMemoryPressure
+    12,         // CrstGlobalStrLiteralMap
+    1,          // CrstHandleTable
+    0,          // CrstHostAssemblyMap
+    3,          // CrstHostAssemblyMapAdd
+    0,          // CrstIbcProfile
+    9,          // CrstIJWFixupData
+    0,          // CrstIJWHash
+    7,          // CrstILStubGen
+    3,          // CrstInlineTrackingMap
+    16,         // CrstInstMethodHashTable
+    0,          // CrstInterfaceVTableMap
+    18,         // CrstInterop
+    4,          // CrstInteropData
+    12,         // CrstIOThreadpoolWorker
+    0,          // CrstIsJMCMethod
+    7,          // CrstISymUnmanagedReader
+    8,          // CrstJit
+    0,          // CrstJitGenericHandleCache
+    -1,         // CrstJitPerf
+    6,          // CrstJumpStubCache
+    0,          // CrstLeafLock
+    -1,         // CrstListLock
+    14,         // CrstLoaderAllocator
+    15,         // CrstLoaderAllocatorReferences
+    0,          // CrstLoaderHeap
+    0,          // CrstMda
+    -1,         // CrstMetadataTracker
+    13,         // CrstMethodDescBackpatchInfoTracker
+    0,          // CrstModIntPairList
+    4,          // CrstModule
+    14,         // CrstModuleFixup
+    3,          // CrstModuleLookupTable
+    0,          // CrstMulticoreJitHash
+    12,         // CrstMulticoreJitManager
+    0,          // CrstMUThunkHash
+    -1,         // CrstNativeBinderInit
+    -1,         // CrstNativeImageCache
+    0,          // CrstNls
+    0,          // CrstNotifyGdb
+    2,          // CrstObjectList
+    0,          // CrstOnEventManager
+    0,          // CrstPatchEntryPoint
+    4,          // CrstPEImage
+    0,          // CrstPEImagePDBStream
+    17,         // CrstPendingTypeLoadEntry
+    0,          // CrstPinHandle
+    0,          // CrstPinnedByrefValidation
+    0,          // CrstProfilerGCRefDataFreeList
+    0,          // CrstProfilingAPIStatus
+    0,          // CrstPublisherCertificate
+    3,          // CrstRCWCache
+    0,          // CrstRCWCleanupList
+    3,          // CrstRCWRefCache
+    4,          // CrstReadyToRunEntryPointToMethodDescMap
+    0,          // CrstReDacl
+    9,          // CrstReflection
+    9,          // CrstReJITDomainTable
+    13,         // CrstReJITGlobalRequest
+    18,         // CrstRemoting
+    3,          // CrstRetThunkCache
+    0,          // CrstRWLock
+    3,          // CrstSavedExceptionInfo
+    0,          // CrstSaveModuleProfileData
+    0,          // CrstSecurityStackwalkCache
+    4,          // CrstSharedAssemblyCreate
+    3,          // CrstSigConvert
+    5,          // CrstSingleUseLock
+    0,          // CrstSpecialStatics
+    0,          // CrstSqmManager
+    0,          // CrstStackSampler
+    -1,         // CrstStressLog
+    0,          // CrstStrongName
+    5,          // CrstStubCache
+    0,          // CrstStubDispatchCache
+    4,          // CrstStubUnwindInfoHeapSegments
+    3,          // CrstSyncBlockCache
+    0,          // CrstSyncHashLock
+    4,          // CrstSystemBaseDomain
+    12,         // CrstSystemDomain
+    0,          // CrstSystemDomainDelayedUnloadList
+    0,          // CrstThreadIdDispenser
+    0,          // CrstThreadpoolEventCache
+    7,          // CrstThreadpoolTimerQueue
+    7,          // CrstThreadpoolWaitThreads
+    12,         // CrstThreadpoolWorker
+    4,          // CrstThreadStaticDataHashTable
+    11,         // CrstThreadStore
+    9,          // CrstTieredCompilation
+    9,          // CrstTPMethodTable
+    3,          // CrstTypeEquivalenceMap
+    7,          // CrstTypeIDMap
+    3,          // CrstUMEntryThunkCache
+    0,          // CrstUMThunkHash
+    3,          // CrstUniqueStack
+    7,          // CrstUnresolvedClassLock
+    3,          // CrstUnwindInfoTableLock
+    3,          // CrstVSDIndirectionCellLock
+    3,          // CrstWinRTFactoryCache
+    3,          // CrstWrapperTemplate
 };
 
 // An array mapping CrstType to a stringized name.
index f7bedff..42e26c9 100644 (file)
@@ -2555,6 +2555,10 @@ void SystemDomain::LoadBaseSystemClasses()
         TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U1))).AsArray()->GetMethodTable();
 
 #ifndef CROSSGEN_COMPILE
+    CrossLoaderAllocatorHashSetup::EnsureTypesLoaded();
+#endif
+
+#ifndef CROSSGEN_COMPILE
     ECall::PopulateManagedStringConstructors();
 #endif // CROSSGEN_COMPILE
 
@@ -3850,13 +3854,6 @@ void AppDomain::Terminate()
     }
 #endif // FEATURE_COMINTEROP
 
-#ifndef CROSSGEN_COMPILE
-    // Recorded entry point slots may point into the virtual call stub manager's heaps, so clear it first
-    GetLoaderAllocator()
-        ->GetMethodDescBackpatchInfoTracker()
-        ->ClearDependencyMethodDescEntryPointSlots(GetLoaderAllocator());
-#endif
-
     if (!IsAtProcessExit())
     {
         // if we're not shutting down everything then clean up the string literals associated
index e8ace28..5286815 100644 (file)
@@ -2187,6 +2187,8 @@ PCODE CodeVersionManager::PublishVersionableCodeIfNecessary(MethodDesc* pMethodD
             pCode = pMethodDesc->PrepareCode(activeVersion);
         }
 
+        MethodDescBackpatchInfoTracker::ConditionalLockHolder lockHolder(pMethodDesc->MayHaveEntryPointSlotsToBackpatch());
+
         // suspend in preparation for publishing if needed
         if (fEESuspend)
         {
diff --git a/src/vm/crossloaderallocatorhash.h b/src/vm/crossloaderallocatorhash.h
new file mode 100644 (file)
index 0000000..0906875
--- /dev/null
@@ -0,0 +1,197 @@
+// 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.
+
+#ifndef CROSSLOADERALLOCATORHASH_H
+#define CROSSLOADERALLOCATORHASH_H
+#ifndef CROSSGEN_COMPILE
+
+#include "gcheaphashtable.h"
+
+class LoaderAllocator;
+
+template <class TKey_, class TValue_>
+class NoRemoveDefaultCrossLoaderAllocatorHashTraits
+{
+public:
+    typedef TKey_ TKey;
+    typedef TValue_ TValue;
+
+    static bool IsNull(const TValue &value) { return value == NULL; }
+    static TValue NullValue() { return NULL; }
+
+#ifndef DACCESS_COMPILE
+    static void SetUsedEntries(TValue* pStartOfValuesData, DWORD entriesInArrayTotal, DWORD usedEntries);
+    static bool AddToValuesInHeapMemory(OBJECTREF *pKeyValueStore, const TKey& key, const TValue& value);
+#endif //!DACCESS_COMPILE
+    static DWORD ComputeUsedEntries(OBJECTREF *pKeyValueStore, DWORD *pEntriesInArrayTotal);
+    template <class Visitor>
+    static bool VisitKeyValueStore(OBJECTREF *pLoaderAllocatorRef, OBJECTREF *pKeyValueStore, Visitor &visitor);
+    static TKey ReadKeyFromKeyValueStore(OBJECTREF *pKeyValueStore);
+};
+
+template <class TKey_, class TValue_>
+class DefaultCrossLoaderAllocatorHashTraits : public NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>
+{
+public:
+    typedef TKey_ TKey;
+    typedef TValue_ TValue;
+
+#ifndef DACCESS_COMPILE
+    static void DeleteValueInHeapMemory(OBJECTREF keyValueStore, const TValue& value);
+#endif //!DACCESS_COMPILE
+};
+
+struct GCHeapHashDependentHashTrackerHashTraits : public DefaultGCHeapHashTraits<true>
+{
+    typedef LoaderAllocator* PtrTypeKey;
+
+    static INT32 Hash(PtrTypeKey *pValue);
+    static INT32 Hash(PTRARRAYREF arr, INT32 index);
+    static bool DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, PtrTypeKey *pKey);
+    static bool IsDeleted(PTRARRAYREF arr, INT32 index, GCHEAPHASHOBJECTREF gcHeap);
+};
+
+typedef GCHeapHash<GCHeapHashDependentHashTrackerHashTraits> GCHeapHashDependentHashTrackerHash;
+
+template<class TRAITS>
+struct KeyToValuesGCHeapHashTraits : public DefaultGCHeapHashTraits<true>
+{
+    template <class TKey>
+    static INT32 Hash(TKey *pValue);
+    static INT32 Hash(PTRARRAYREF arr, INT32 index);
+
+    template<class TKey>
+    static bool DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, TKey *pKey);
+};
+
+// Hashtable of key to a list of values where the key may live in a different loader allocator
+// than the value and this should not keep the loaderallocator of the value alive. The type of
+// keys/values is defined via the TRAITS template argument, but must be non-gc pointers, and
+// must be copyable without a copy constructor/require a destructor.
+//
+// This is managed via a series of different hashtables and data structures that are carefully
+// engineered to be relatively memory efficient, yet still provide the ability to safely use
+// the hashtable to hold relationships across LoaderAllocators which are not generally safe.
+//
+// In particular, given LoaderAllocator LA1 and LA2, where a reference to LA1 is not
+// guaranteed to keep LA2 alive, this data structure can permit a pointer to an object which
+// is defined as part of LA1 to be used as a key to find a pointer to an object that has the
+// same lifetime as LA2.
+//
+// This data structure exposes Remove api's, but its primary use case is the combination of
+// the Add and VisitValuesOfKey apis.
+//
+// To use Add, simply, call Add(TKey key, TValue value). This will add to the list of values
+// associated with a key. The Add api should be called on a key's which are associated with
+// the same LoaderAllocator as the CrossLoaderAllocatorHash.
+//
+// VisitValuesOfKey will visit all values that have the same key.
+//
+// IMPLEMENTATION DESIGN
+// This data structure is a series of hashtables and lists.
+// 
+// In general, this data structure builds a set of values associated with a key per
+// LoaderAllocator. The lists per loader allocator are controlled via the TRAITS template. The
+// TRAITS specify how the individual lists are handled, and do the copying in and out of the data
+// structures. It is not expected that additional traits implementations will be needed for use,
+// unless duplicate prevention is needed.
+//
+// BASIC STRUCTURE
+//
+// m_keyToDependentTrackersHash - Hashtable of key -> (list of values in primary loader allocator,
+//                                                   hashtable of DependentTrackers)
+//
+//   For each key in the table, there is at list of values in the primary loader allocator,
+//   and optionally there may be a hashtable of dependent trackers
+//
+// m_loaderAllocatorToDependentTrackerHash - Hashtable of LoaderAllocator to DependentTracker. Used to find
+// dependent trackers for insertion into per key sets.
+//
+// The DependentTracker is an object (with a finalizer) which is associated with a specific
+// LoaderAllocator, and uses a DependentHandle to hold onto  a hashtable from Key to List of
+// Values (for a specific LoaderAllocator). This dependent handle will keep that hashtable alive
+// as long as the associated LoaderAllocator is live.
+//
+// The DependentTracker hashes (both the m_loaderAllocatorToDependentTrackerHash, and the per key hashes) are
+// implemented via a hashtable which is "self-cleaning". In particular as the hashtable is
+// walked for Add/Visit/Remove operations, if a DependentTracker is found which where the
+// DependentHandle has detected that the LoaderAllocator has been freed, then the entry in
+// the hashtable will set itself to the DELETED state. This cleaning operation will not occur
+// eagerly, but it should prevent unbounded size growth as collectible LoaderAllocators are
+// allocated and freed.
+//
+// Memory efficiency of this data structure.
+//  - This data structure is reasonably memory efficient. If many values share the same key
+//    then the memory efficiency per key trends toward 1.3333 * sizeof(Value). Otherwise basic
+//    cost per key/value pair (assuming they are pointer sized has an overhead of about 4 
+//    pointers + key/value data size.)
+template <class TRAITS>
+class CrossLoaderAllocatorHash
+{
+private:
+    typedef typename TRAITS::TKey TKey;
+    typedef typename TRAITS::TValue TValue;
+    typedef GCHeapHash<KeyToValuesGCHeapHashTraits<TRAITS>> KeyToValuesGCHeapHash;
+
+public:
+
+#ifndef DACCESS_COMPILE
+    // Add an entry to the CrossLoaderAllocatorHash, the default implementation of does DefaultCrossLoaderAllocatorHashTraits will not check for duplicates.
+    void Add(TKey key, TValue value, LoaderAllocator *pLoaderAllocatorOfValue);
+
+    // Remove an entry to the CrossLoaderAllocatorHash, only removes one entry
+    void Remove(TKey key, TValue value, LoaderAllocator *pLoaderAllocatorOfValue);
+
+    // Remove all entries that can be looked up by key
+    void RemoveAll(TKey key);
+#endif
+
+    // Using visitor walk all values associated with a given key. The visitor
+    // is expected to implement bool operator ()(OBJECTREF keepAlive, TKey key, TValue value).
+    // Return false from that function to stop visitation.
+    // This can be done simply by utilizing a lambda, or if a lambda cannot be used, a functor will do.
+    // The value of "value" in this case must not escape from the visitor object
+    // unless the keepAlive OBJECTREF is also kept alive
+    template <class Visitor>
+    bool VisitValuesOfKey(TKey key, Visitor &visitor);
+
+    // Visit all key/value pairs
+    template <class Visitor>
+    bool VisitAllKeyValuePairs(Visitor &visitor);
+
+    // Initialize this CrossLoaderAllocatorHash to be associated with a specific LoaderAllocator
+    // Must be called before any use of Add
+    void Init(LoaderAllocator *pAssociatedLoaderAllocator);
+
+private:
+#ifndef DACCESS_COMPILE
+    void EnsureManagedObjectsInitted();
+    LAHASHDEPENDENTHASHTRACKERREF GetDependentTrackerForLoaderAllocator(LoaderAllocator* pLoaderAllocator);
+    GCHEAPHASHOBJECTREF GetKeyToValueCrossLAHashForHashkeyToTrackers(LAHASHKEYTOTRACKERSREF hashKeyToTrackersUnsafe, LoaderAllocator* pValueLoaderAllocator);
+#endif // !DACCESS_COMPILE
+    
+    template <class Visitor>
+    static bool VisitKeyValueStore(OBJECTREF *pLoaderAllocatorRef, OBJECTREF *pKeyValueStore, Visitor &visitor);
+    template <class Visitor>
+    static bool VisitTracker(TKey key, LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe, Visitor &visitor);
+    template <class Visitor>
+    static bool VisitTrackerAllEntries(LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe, Visitor &visitor);
+    template <class Visitor>
+    static bool VisitKeyToTrackerAllEntries(OBJECTREF hashKeyEntryUnsafe, Visitor &visitor);
+    static void DeleteEntryTracker(TKey key, LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe);
+
+private:
+    LoaderAllocator *m_pLoaderAllocator = 0;
+    OBJECTHANDLE m_loaderAllocatorToDependentTrackerHash = 0;
+    OBJECTHANDLE m_keyToDependentTrackersHash = 0;
+};
+
+class CrossLoaderAllocatorHashSetup
+{
+public:
+    inline static void EnsureTypesLoaded();
+};
+
+#endif // !CROSSGEN_COMPILE
+#endif // CROSSLOADERALLOCATORHASH_H
diff --git a/src/vm/crossloaderallocatorhash.inl b/src/vm/crossloaderallocatorhash.inl
new file mode 100644 (file)
index 0000000..51bce4e
--- /dev/null
@@ -0,0 +1,1216 @@
+// 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.
+
+#ifndef CROSSLOADERALLOCATORHASH_INL
+#define CROSSLOADERALLOCATORHASH_INL
+#ifdef CROSSLOADERALLOCATORHASH_H
+#ifndef CROSSGEN_COMPILE
+
+#include "gcheaphashtable.inl"
+
+template <class TKey_, class TValue_>
+/*static*/ DWORD NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::ComputeUsedEntries(OBJECTREF *pKeyValueStore, DWORD *pEntriesInArrayTotal)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    DWORD entriesInArrayTotal = (((I1ARRAYREF)*pKeyValueStore)->GetNumComponents() - sizeof(TKey))/sizeof(TValue);
+    DWORD usedEntries;
+    TValue* pStartOfValuesData = (TValue*)(((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements() + sizeof(TKey));
+
+    if (entriesInArrayTotal == 0)
+    {
+        usedEntries = 0;
+    }
+    else if ((entriesInArrayTotal >= 2) && (pStartOfValuesData[entriesInArrayTotal - 2] == (TValue)0))
+    {
+        usedEntries = (DWORD)pStartOfValuesData[entriesInArrayTotal - 1];
+    }
+    else if (pStartOfValuesData[entriesInArrayTotal - 1] == (TValue)0)
+    {
+        usedEntries = entriesInArrayTotal - 1;
+    }
+    else
+    {
+        usedEntries = entriesInArrayTotal;
+    }
+
+    *pEntriesInArrayTotal = entriesInArrayTotal;
+    return usedEntries;
+}
+
+#ifndef DACCESS_COMPILE
+template <class TKey_, class TValue_>
+/*static*/ void NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::SetUsedEntries(TValue* pStartOfValuesData, DWORD entriesInArrayTotal, DWORD usedEntries)
+{
+    if (usedEntries < entriesInArrayTotal)
+    {
+        if (usedEntries == (entriesInArrayTotal - 1))
+        {
+            pStartOfValuesData[entriesInArrayTotal - 1] = (TValue)0;
+        }
+        else
+        {
+            pStartOfValuesData[entriesInArrayTotal - 1] = (TValue)(usedEntries);
+            pStartOfValuesData[entriesInArrayTotal - 2] = (TValue)0;
+        }
+    }
+}
+
+template <class TKey_, class TValue_>
+/*static*/ bool NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::AddToValuesInHeapMemory(OBJECTREF *pKeyValueStore, const TKey& key, const TValue& value)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    static_assert(sizeof(TKey)==sizeof(TValue), "Assume keys and values are the same size");
+
+    bool updatedKeyValueStore = false;
+
+    if (*pKeyValueStore == NULL)
+    {
+        *pKeyValueStore = AllocatePrimitiveArray(ELEMENT_TYPE_I1, IsNull(value) ? sizeof(TKey) : sizeof(TKey) + sizeof(TValue), FALSE);
+        updatedKeyValueStore = true;
+        TKey* pKeyLoc = (TKey*)((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements();
+        *pKeyLoc = key;
+        if (!IsNull(value))
+        {
+            TValue* pValueLoc = (TValue*)(((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements() + sizeof(TKey));
+            *pValueLoc = value;
+        }
+    }
+    else if (!IsNull(value))
+    {
+        DWORD entriesInArrayTotal;
+        DWORD usedEntries = ComputeUsedEntries(pKeyValueStore, &entriesInArrayTotal);
+
+        if (usedEntries == entriesInArrayTotal)
+        {
+            // There isn't free space. Build a new, bigger array with the existing data 
+            DWORD newSize;
+            if (usedEntries < 8)
+                newSize = usedEntries + 1; // Grow very slowly initially. The cost of allocation/copy is cheap, and this holds very tight on memory usage
+            else
+                newSize = usedEntries * 2;
+
+            if (newSize < usedEntries)
+                COMPlusThrow(kOverflowException);
+
+            // Allocate the new array.
+            I1ARRAYREF newKeyValueStore = (I1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I1, newSize*sizeof(TValue) + sizeof(TKey), FALSE);
+
+            // Since, AllocatePrimitiveArray may have triggered a GC, recapture all data pointers from GC objects
+            void* pStartOfNewArray = newKeyValueStore->GetDirectPointerToNonObjectElements();
+            void* pStartOfOldArray = ((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements();
+
+            memcpyNoGCRefs(pStartOfNewArray, pStartOfOldArray, ((I1ARRAYREF)*pKeyValueStore)->GetNumComponents());
+
+            *pKeyValueStore = (OBJECTREF)newKeyValueStore;
+            updatedKeyValueStore = true;
+
+            entriesInArrayTotal = newSize;
+        }
+
+        // There is free space. Append on the end
+        TValue* pStartOfValuesData = (TValue*)(((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements() + sizeof(TKey));
+        SetUsedEntries(pStartOfValuesData, entriesInArrayTotal, usedEntries + 1);
+        pStartOfValuesData[usedEntries] = value;
+    }
+
+    return updatedKeyValueStore;
+}
+#endif //!DACCESS_COMPILE
+
+template <class TKey_, class TValue_>
+/*static*/ TKey_ NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::ReadKeyFromKeyValueStore(OBJECTREF *pKeyValueStore)
+{
+    WRAPPER_NO_CONTRACT;
+
+    TKey* pKeyLoc = (TKey*)((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements();
+    return *pKeyLoc;
+}
+
+template <class TKey_, class TValue_>
+template <class Visitor>
+/*static*/ bool NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::VisitKeyValueStore(OBJECTREF *pLoaderAllocatorRef, OBJECTREF *pKeyValueStore, Visitor &visitor)
+{
+    WRAPPER_NO_CONTRACT;
+
+    DWORD entriesInArrayTotal;
+    DWORD usedEntries = ComputeUsedEntries(pKeyValueStore, &entriesInArrayTotal);
+
+    for (DWORD index = 0; index < usedEntries; ++index)
+    {
+        // Capture pKeyLoc and pStartOfValuesData inside of loop, as we aren't protecting these pointers into the GC heap, so they
+        // are not permitted to live across the call to visitor (in case visitor triggers a GC)
+        TKey* pKeyLoc = (TKey*)((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements();
+        TValue* pStartOfValuesData = (TValue*)(((I1ARRAYREF)*pKeyValueStore)->GetDirectPointerToNonObjectElements() + sizeof(TKey));
+
+        if (!visitor(*pLoaderAllocatorRef, *pKeyLoc, pStartOfValuesData[index]))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+#ifndef DACCESS_COMPILE
+template <class TKey_, class TValue_>
+/*static*/ void DefaultCrossLoaderAllocatorHashTraits<TKey_, TValue_>::DeleteValueInHeapMemory(OBJECTREF keyValueStore, const TValue& value)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    // TODO: Consider optimizing this by changing the add to ensure that the 
+    // values list is sorted, and then doing a binary search for the value instead
+    // of the linear search
+
+    DWORD entriesInArrayTotal;
+    DWORD usedEntries = NoRemoveDefaultCrossLoaderAllocatorHashTraits<TKey,TValue>::ComputeUsedEntries(&keyValueStore, &entriesInArrayTotal);
+    TValue* pStartOfValuesData = (TValue*)(((I1ARRAYREF)keyValueStore)->GetDirectPointerToNonObjectElements() + sizeof(TKey));
+
+    for (DWORD iEntry = 0; iEntry < usedEntries; iEntry++)
+    {
+        if (pStartOfValuesData[iEntry] == value)
+        {
+            memmove(pStartOfValuesData + iEntry, pStartOfValuesData + iEntry + 1, (usedEntries - iEntry - 1) * sizeof(TValue));
+            SetUsedEntries(pStartOfValuesData, entriesInArrayTotal, usedEntries - 1);
+            return;
+        }
+    }
+}
+#endif //!DACCESS_COMPILE
+
+/*static*/ inline INT32 GCHeapHashDependentHashTrackerHashTraits::Hash(PtrTypeKey *pValue)
+{
+    LIMITED_METHOD_CONTRACT;
+    return (INT32)*pValue;
+}
+
+/*static*/ inline INT32 GCHeapHashDependentHashTrackerHashTraits::Hash(PTRARRAYREF arr, INT32 index)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    LAHASHDEPENDENTHASHTRACKERREF value = (LAHASHDEPENDENTHASHTRACKERREF)arr->GetAt(index);
+    LoaderAllocator *pLoaderAllocator = value->GetLoaderAllocatorUnsafe();
+    return Hash(&pLoaderAllocator);
+}
+
+/*static*/ inline bool GCHeapHashDependentHashTrackerHashTraits::DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, PtrTypeKey *pKey)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    LAHASHDEPENDENTHASHTRACKERREF value = (LAHASHDEPENDENTHASHTRACKERREF)arr->GetAt(index);
+
+    return value->IsTrackerFor(*pKey);
+}
+
+/*static*/ inline bool GCHeapHashDependentHashTrackerHashTraits::IsDeleted(PTRARRAYREF arr, INT32 index, GCHEAPHASHOBJECTREF gcHeap)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    OBJECTREF valueInHeap = arr->GetAt(index);
+
+    if (valueInHeap == NULL)
+        return false;
+
+    if (gcHeap == valueInHeap)
+        return true;
+
+    // This is a tricky bit of logic used which detects freed loader allocators lazily
+    // and deletes them from the GCHeapHash while looking up or otherwise walking the hashtable
+    // for any purpose.
+    LAHASHDEPENDENTHASHTRACKERREF value = (LAHASHDEPENDENTHASHTRACKERREF)valueInHeap;
+    if (!value->IsLoaderAllocatorLive())
+    {
+#ifndef DACCESS_COMPILE
+        arr->SetAt(index, gcHeap);
+        gcHeap->DecrementCount(true);
+#endif // DACCESS_COMPILE
+
+        return true;
+    }
+
+    return false;
+}
+
+template<class TRAITS>
+template <class TKey>
+/*static*/ INT32 KeyToValuesGCHeapHashTraits<TRAITS>::Hash(TKey *pValue)
+{
+    LIMITED_METHOD_CONTRACT;
+    return (INT32)(DWORD)*pValue;
+}
+
+template<class TRAITS>
+/*static*/ inline INT32 KeyToValuesGCHeapHashTraits<TRAITS>::Hash(PTRARRAYREF arr, INT32 index)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    OBJECTREF hashKeyEntry = arr->GetAt(index);
+    LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+    OBJECTREF keyValueStore;
+
+    if (hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+    {
+        hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)hashKeyEntry;
+        keyValueStore = hashKeyToTrackers->_laLocalKeyValueStore;
+    }
+    else
+    {
+        keyValueStore = hashKeyEntry;
+    }
+
+    typename TRAITS::TKey key = TRAITS::ReadKeyFromKeyValueStore(&keyValueStore);
+    return Hash(&key);
+}
+
+template<class TRAITS>
+template<class TKey>
+/*static*/ bool KeyToValuesGCHeapHashTraits<TRAITS>::DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, TKey *pKey)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    OBJECTREF hashKeyEntry = arr->GetAt(index);
+    LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+    OBJECTREF keyValueStore;
+
+    if (hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+    {
+        hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)hashKeyEntry;
+        keyValueStore = hashKeyToTrackers->_laLocalKeyValueStore;
+    }
+    else
+    {
+        keyValueStore = hashKeyEntry;
+    }
+
+    TKey key = TRAITS::ReadKeyFromKeyValueStore(&keyValueStore);
+
+    return key == *pKey;
+}
+
+#ifndef DACCESS_COMPILE
+template <class TRAITS>
+void CrossLoaderAllocatorHash<TRAITS>::Add(TKey key, TValue value, LoaderAllocator *pLoaderAllocatorOfValue)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+
+    struct {
+        KeyToValuesGCHeapHash keyToTrackersHash;
+        KeyToValuesGCHeapHash keyToValuePerLAHash;
+        OBJECTREF keyValueStore;
+        OBJECTREF hashKeyEntry;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    GCPROTECT_BEGIN(gc)
+    {
+        EnsureManagedObjectsInitted();
+
+        bool addToKeyValuesHash = false;
+        // This data structure actually doesn't have this invariant, but it is expected that uses of this
+        // data structure will require that the key's loader allocator is the same as that of this data structure.
+        _ASSERTE(key->GetLoaderAllocator() == m_pLoaderAllocator);
+
+        gc.keyToTrackersHash = KeyToValuesGCHeapHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_keyToDependentTrackersHash));
+        INT32 index = gc.keyToTrackersHash.GetValueIndex(&key);
+
+        if (index == -1)
+        {
+            addToKeyValuesHash = true;
+            TRAITS::AddToValuesInHeapMemory(&gc.keyValueStore, key, pLoaderAllocatorOfValue == m_pLoaderAllocator ? value : TRAITS::NullValue());
+
+            if (pLoaderAllocatorOfValue != m_pLoaderAllocator)
+            {
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)AllocateObject(MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS));
+                SetObjectReference(&gc.hashKeyToTrackers->_laLocalKeyValueStore, gc.keyValueStore, GetAppDomain());
+                gc.hashKeyEntry = gc.hashKeyToTrackers;
+            }
+            else
+            {
+                gc.hashKeyEntry = gc.keyValueStore;
+            }
+
+            gc.keyToTrackersHash.Add(&key, [&gc](PTRARRAYREF arr, INT32 index)
+            {
+                arr->SetAt(index, (OBJECTREF)gc.hashKeyEntry);
+            });
+        }
+        else
+        {
+            gc.keyToTrackersHash.GetElement(index, gc.hashKeyEntry);
+
+            if (gc.hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+            {
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)gc.hashKeyEntry;
+                gc.keyValueStore = gc.hashKeyToTrackers->_laLocalKeyValueStore;
+            }
+            else
+            {
+                gc.keyValueStore = gc.hashKeyEntry;
+            }
+
+            bool updatedKeyValueStore = false;
+
+            if (pLoaderAllocatorOfValue == m_pLoaderAllocator)
+            {
+                updatedKeyValueStore = TRAITS::AddToValuesInHeapMemory(&gc.keyValueStore, key, value);
+            }
+
+            if (updatedKeyValueStore)
+            {
+                if (gc.hashKeyToTrackers != NULL)
+                {
+                    SetObjectReference(&gc.hashKeyToTrackers->_laLocalKeyValueStore, gc.keyValueStore, GetAppDomain());
+                }
+                else
+                {
+                    gc.hashKeyEntry = gc.keyValueStore;
+                    gc.keyToTrackersHash.SetElement(index, gc.hashKeyEntry);
+                }
+            }
+        }
+
+        // If the LoaderAllocator matches, we've finished adding by now, otherwise, we need to get the remove hash and work with that
+        if (pLoaderAllocatorOfValue != m_pLoaderAllocator)
+        {
+            if (gc.hashKeyToTrackers == NULL)
+            {
+                // Nothing has yet caused the trackers proxy object to be setup. Create it now, and update the keyToTrackersHash
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)AllocateObject(MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS));
+                SetObjectReference(&gc.hashKeyToTrackers->_laLocalKeyValueStore, gc.keyValueStore, GetAppDomain());
+                gc.hashKeyEntry = gc.hashKeyToTrackers;
+                gc.keyToTrackersHash.SetElement(index, gc.hashKeyEntry);
+            }
+
+            // Must add it to the cross LA structure
+            GCHEAPHASHOBJECTREF gcheapKeyToValue = GetKeyToValueCrossLAHashForHashkeyToTrackers(gc.hashKeyToTrackers, pLoaderAllocatorOfValue);
+
+            gc.keyToValuePerLAHash = KeyToValuesGCHeapHash(gcheapKeyToValue);
+
+            INT32 indexInKeyValueHash = gc.keyToValuePerLAHash.GetValueIndex(&key);
+            if (indexInKeyValueHash != -1)
+            {
+                gc.keyToValuePerLAHash.GetElement(indexInKeyValueHash, gc.keyValueStore);
+
+                if (TRAITS::AddToValuesInHeapMemory(&gc.keyValueStore, key, value))
+                {
+                    gc.keyToValuePerLAHash.SetElement(indexInKeyValueHash, gc.keyValueStore);
+                }
+            }
+            else
+            {
+                gc.keyValueStore = NULL;
+                TRAITS::AddToValuesInHeapMemory(&gc.keyValueStore, key, value);
+
+                gc.keyToValuePerLAHash.Add(&key, [&gc](PTRARRAYREF arr, INT32 index)
+                {
+                    arr->SetAt(index, gc.keyValueStore);
+                });
+            }
+        }
+    }
+    GCPROTECT_END();
+}
+#endif // !DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+template <class TRAITS>
+void CrossLoaderAllocatorHash<TRAITS>::Remove(TKey key, TValue value, LoaderAllocator *pLoaderAllocatorOfValue)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    // This data structure actually doesn't have this invariant, but it is expected that uses of this
+    // data structure will require that the key's loader allocator is the same as that of this data structure.
+    _ASSERTE(key->GetLoaderAllocator() == m_pLoaderAllocator);
+
+    if (m_keyToDependentTrackersHash == NULL)
+    {
+        // If the heap objects haven't been initted, then there is nothing to delete
+        return;
+    }
+
+    struct {
+        KeyToValuesGCHeapHash keyToTrackersHash;
+        KeyToValuesGCHeapHash keyToValuePerLAHash;
+        OBJECTREF hashKeyEntry;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+        OBJECTREF keyValueStore;
+    } gc;
+
+    ZeroMemory(&gc, sizeof(gc));
+    GCPROTECT_BEGIN(gc)
+    {
+        gc.keyToTrackersHash = KeyToValuesGCHeapHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_keyToDependentTrackersHash));
+        INT32 index = gc.keyToTrackersHash.GetValueIndex(&key);
+
+        if (index != -1)
+        {
+            gc.keyToTrackersHash.GetElement(index, gc.hashKeyEntry);
+
+            if (gc.hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+            {
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)gc.hashKeyEntry;
+                gc.keyValueStore = gc.hashKeyToTrackers->_laLocalKeyValueStore;
+            }
+            else
+            {
+                gc.keyValueStore = gc.hashKeyEntry;
+            }
+
+            // Check to see if value can be added to this data structure directly.
+            if (m_pLoaderAllocator == pLoaderAllocatorOfValue)
+            {
+                TRAITS::DeleteValueInHeapMemory(gc.keyValueStore, value);
+            }
+            else if (gc.hashKeyToTrackers != NULL)
+            {
+                // Must remove it from the cross LA structure
+                GCHEAPHASHOBJECTREF gcheapKeyToValue = GetKeyToValueCrossLAHashForHashkeyToTrackers(gc.hashKeyToTrackers, pLoaderAllocatorOfValue);
+
+                gc.keyToValuePerLAHash = KeyToValuesGCHeapHash(gcheapKeyToValue);
+
+                INT32 indexInKeyValueHash = gc.keyToValuePerLAHash.GetValueIndex(&key);
+                if (indexInKeyValueHash != -1)
+                {
+                    gc.keyToValuePerLAHash.GetElement(indexInKeyValueHash, gc.keyValueStore);
+                    TRAITS::DeleteValueInHeapMemory(gc.keyValueStore, value);
+                }
+            }
+        }
+    }
+    GCPROTECT_END();
+}
+#endif // !DACCESS_COMPILE
+
+template <class TRAITS>
+template <class Visitor>
+bool CrossLoaderAllocatorHash<TRAITS>::VisitValuesOfKey(TKey key, Visitor &visitor)
+{
+    WRAPPER_NO_CONTRACT;
+
+    class VisitIndividualEntryKeyValueHash
+    {
+        public:
+        TKey m_key;
+        Visitor *m_pVisitor;
+        GCHeapHashDependentHashTrackerHash *m_pDependentTrackerHash;
+
+        VisitIndividualEntryKeyValueHash(TKey key, Visitor *pVisitor,  GCHeapHashDependentHashTrackerHash *pDependentTrackerHash) : 
+            m_key(key),
+            m_pVisitor(pVisitor),
+            m_pDependentTrackerHash(pDependentTrackerHash)
+            {}
+
+        bool operator()(INT32 index)
+        {
+            WRAPPER_NO_CONTRACT;
+
+            LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+            m_pDependentTrackerHash->GetElement(index, dependentTracker);
+            return VisitTracker(m_key, dependentTracker, *m_pVisitor);
+        }
+    };
+
+    // This data structure actually doesn't have this invariant, but it is expected that uses of this
+    // data structure will require that the key's loader allocator is the same as that of this data structure.
+    _ASSERTE(key->GetLoaderAllocator() == m_pLoaderAllocator);
+
+    // Check to see that something has been added
+    if (m_keyToDependentTrackersHash == NULL)
+        return true;
+
+    bool result = true;
+    struct 
+    {
+        KeyToValuesGCHeapHash keyToTrackersHash;
+        GCHeapHashDependentHashTrackerHash dependentTrackerHash;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTrackerMaybe;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+        OBJECTREF hashKeyEntry;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+        OBJECTREF keyValueStore;
+        OBJECTREF nullref;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    GCPROTECT_BEGIN(gc)
+    {
+        gc.keyToTrackersHash = KeyToValuesGCHeapHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_keyToDependentTrackersHash));
+        INT32 index = gc.keyToTrackersHash.GetValueIndex(&key);
+        if (index != -1)
+        {
+            // We have an entry in the hashtable for the key/dependenthandle.
+            gc.keyToTrackersHash.GetElement(index, gc.hashKeyEntry);
+
+            if (gc.hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+            {
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)gc.hashKeyEntry;
+                gc.keyValueStore = gc.hashKeyToTrackers->_laLocalKeyValueStore;
+            }
+            else
+            {
+                gc.keyValueStore = gc.hashKeyEntry;
+            }
+
+            // Now gc.hashKeyToTrackers is filled in and keyValueStore
+
+            // visit local entries
+            result = VisitKeyValueStore(&gc.nullref, &gc.keyValueStore, visitor);
+
+            if (gc.hashKeyToTrackers != NULL)
+            {
+                // Is there a single dependenttracker here, or a set.
+
+                if (gc.hashKeyToTrackers->_trackerOrTrackerSet->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHDEPENDENTHASHTRACKER))
+                {
+                    gc.dependentTracker = (LAHASHDEPENDENTHASHTRACKERREF)gc.hashKeyToTrackers->_trackerOrTrackerSet;
+                    result = VisitTracker(key, gc.dependentTracker, visitor);
+                }
+                else
+                {
+                    gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash(gc.hashKeyToTrackers->_trackerOrTrackerSet);
+                    VisitIndividualEntryKeyValueHash visitIndivididualKeys(key, &visitor, &gc.dependentTrackerHash);
+                    result = gc.dependentTrackerHash.VisitAllEntryIndices(visitIndivididualKeys);
+                }
+            }
+        }
+    }
+    GCPROTECT_END();
+
+    return result;
+}
+
+template <class TRAITS>
+template <class Visitor>
+bool CrossLoaderAllocatorHash<TRAITS>::VisitAllKeyValuePairs(Visitor &visitor)
+{
+    CONTRACTL
+    {
+        MODE_COOPERATIVE;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    class VisitAllEntryKeyToDependentTrackerHash
+    {
+        public:
+        Visitor *m_pVisitor;
+        KeyToValuesGCHeapHash *m_pKeyToTrackerHash;
+
+        VisitAllEntryKeyToDependentTrackerHash(Visitor *pVisitor,  KeyToValuesGCHeapHash *pKeyToTrackerHash) : 
+            m_pVisitor(pVisitor),
+            m_pKeyToTrackerHash(pKeyToTrackerHash)
+            {}
+
+        bool operator()(INT32 index)
+        {
+            WRAPPER_NO_CONTRACT;
+
+            OBJECTREF hashKeyEntry;
+            m_pKeyToTrackerHash->GetElement(index, hashKeyEntry);
+            return VisitKeyToTrackerAllEntries(hashKeyEntry, *m_pVisitor);
+        }
+    };
+
+    class VisitAllEntryDependentTrackerHash
+    {
+        public:
+        Visitor *m_pVisitor;
+        GCHeapHashDependentHashTrackerHash *m_pDependentTrackerHash;
+
+        VisitAllEntryDependentTrackerHash(Visitor *pVisitor,  GCHeapHashDependentHashTrackerHash *pDependentTrackerHash) : 
+            m_pVisitor(pVisitor),
+            m_pDependentTrackerHash(pDependentTrackerHash)
+            {}
+
+        bool operator()(INT32 index)
+        {
+            WRAPPER_NO_CONTRACT;
+
+            LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+            m_pDependentTrackerHash->GetElement(index, dependentTracker);
+            return VisitTrackerAllEntries(dependentTracker, *m_pVisitor);
+        }
+    };
+
+    struct 
+    {
+        KeyToValuesGCHeapHash keyToTrackersHash;
+        GCHeapHashDependentHashTrackerHash dependentTrackerHash;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    bool result = true;
+    GCPROTECT_BEGIN(gc)
+    {
+        if (m_keyToDependentTrackersHash != NULL)
+        {
+            // Visit all local entries
+            gc.keyToTrackersHash = KeyToValuesGCHeapHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_keyToDependentTrackersHash));
+            VisitAllEntryKeyToDependentTrackerHash visitAllEntryKeys(&visitor, &gc.keyToTrackersHash);
+            result = gc.keyToTrackersHash.VisitAllEntryIndices(visitAllEntryKeys);
+        }
+
+        if (m_loaderAllocatorToDependentTrackerHash != NULL)
+        {
+            // Visit the non-local data
+            gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_loaderAllocatorToDependentTrackerHash));
+            VisitAllEntryDependentTrackerHash visitDependentTrackers(&visitor, &gc.dependentTrackerHash);
+            result = gc.dependentTrackerHash.VisitAllEntryIndices(visitDependentTrackers);
+        }
+    }
+    GCPROTECT_END();
+
+    return result;
+}
+
+#ifndef DACCESS_COMPILE
+template <class TRAITS>
+void CrossLoaderAllocatorHash<TRAITS>::RemoveAll(TKey key)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    class DeleteIndividualEntryKeyValueHash
+    {
+        public:
+        TKey m_key;
+        GCHeapHashDependentHashTrackerHash *m_pDependentTrackerHash;
+
+        DeleteIndividualEntryKeyValueHash(TKey key, GCHeapHashDependentHashTrackerHash *pDependentTrackerHash) : 
+            m_key(key),
+            m_pDependentTrackerHash(pDependentTrackerHash)
+            {}
+
+        bool operator()(INT32 index)
+        {
+            WRAPPER_NO_CONTRACT;
+
+            LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+            m_pDependentTrackerHash->GetElement(index, dependentTracker);
+            DeleteEntryTracker(m_key, dependentTracker);
+            return true;
+        }
+    };
+
+    // This data structure actually doesn't have this invariant, but it is expected that uses of this
+    // data structure will require that the key's loader allocator is the same as that of this data structure.
+    _ASSERTE(key->GetLoaderAllocator() == m_pLoaderAllocator);
+
+    if (m_keyToDependentTrackersHash == NULL)
+    {
+        return; // Nothing was ever added, so removing all is easy
+    }
+
+    struct 
+    {
+        KeyToValuesGCHeapHash keyToTrackersHash;
+        GCHeapHashDependentHashTrackerHash dependentTrackerHash;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+        OBJECTREF hashKeyEntry;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+        OBJECTREF keyValueStore;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    GCPROTECT_BEGIN(gc)
+    {
+        gc.keyToTrackersHash = KeyToValuesGCHeapHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_keyToDependentTrackersHash));
+        INT32 index = gc.keyToTrackersHash.GetValueIndex(&key);
+        if (index != -1)
+        {
+            // We have an entry in the hashtable for the key/dependenthandle.
+            gc.keyToTrackersHash.GetElement(index, gc.hashKeyEntry);
+
+            if (gc.hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+            {
+                gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)gc.hashKeyEntry;
+                gc.keyValueStore = gc.hashKeyToTrackers->_laLocalKeyValueStore;
+            }
+            else
+            {
+                gc.keyValueStore = gc.hashKeyEntry;
+            }
+
+            // Now gc.hashKeyToTrackers is filled in 
+
+            if (gc.hashKeyToTrackers != NULL)
+            {
+                // Is there a single dependenttracker here, or a set.
+
+                if (gc.hashKeyToTrackers->_trackerOrTrackerSet->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHDEPENDENTHASHTRACKER))
+                {
+                    gc.dependentTracker = (LAHASHDEPENDENTHASHTRACKERREF)gc.hashKeyToTrackers->_trackerOrTrackerSet;
+                    DeleteEntryTracker(key, gc.dependentTracker);
+                }
+                else
+                {
+                    gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash(gc.hashKeyToTrackers->_trackerOrTrackerSet);
+                    DeleteIndividualEntryKeyValueHash deleteIndividualKeyValues(key, &gc.dependentTrackerHash);
+                    gc.dependentTrackerHash.VisitAllEntryIndices(deleteIndividualKeyValues);
+                }
+            }
+
+            // Remove entry from key to tracker hash
+            gc.keyToTrackersHash.DeleteEntry(&key);
+        }
+    }
+    GCPROTECT_END();
+}
+#endif // !DACCESS_COMPILE
+
+template <class TRAITS>
+void CrossLoaderAllocatorHash<TRAITS>::Init(LoaderAllocator *pAssociatedLoaderAllocator)
+{
+    LIMITED_METHOD_CONTRACT;
+    m_pLoaderAllocator = pAssociatedLoaderAllocator;
+}
+
+template <class TRAITS>
+template <class Visitor>
+/*static*/ bool CrossLoaderAllocatorHash<TRAITS>::VisitKeyValueStore(OBJECTREF *pLoaderAllocatorRef, OBJECTREF *pKeyValueStore, Visitor &visitor)
+{
+    WRAPPER_NO_CONTRACT;
+
+    return TRAITS::VisitKeyValueStore(pLoaderAllocatorRef, pKeyValueStore, visitor);
+}
+
+template <class TRAITS>
+template <class Visitor>
+/*static*/ bool CrossLoaderAllocatorHash<TRAITS>::VisitTracker(TKey key, LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe, Visitor &visitor)
+{
+    CONTRACTL
+    {
+        MODE_COOPERATIVE;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    struct 
+    {
+        LAHASHDEPENDENTHASHTRACKERREF tracker;
+        OBJECTREF loaderAllocatorRef;
+        GCHEAPHASHOBJECTREF keyToValuesHashObject;
+        KeyToValuesGCHeapHash keyToValuesHash;
+        OBJECTREF keyValueStore;
+    }gc;
+
+    ZeroMemory(&gc, sizeof(gc));
+    gc.tracker = trackerUnsafe;
+
+    bool result = true;
+
+    GCPROTECT_BEGIN(gc);
+    {
+        gc.tracker->GetDependentAndLoaderAllocator(&gc.loaderAllocatorRef, &gc.keyToValuesHashObject);
+        if (gc.keyToValuesHashObject != NULL)
+        {
+            gc.keyToValuesHash = KeyToValuesGCHeapHash(gc.keyToValuesHashObject);
+            INT32 indexInKeyValueHash = gc.keyToValuesHash.GetValueIndex(&key);
+            if (indexInKeyValueHash != -1)
+            {
+                gc.keyToValuesHash.GetElement(indexInKeyValueHash, gc.keyValueStore);
+
+                result = VisitKeyValueStore(&gc.loaderAllocatorRef, &gc.keyValueStore, visitor);
+            }
+        }
+    }
+    GCPROTECT_END();
+
+    return result;
+}
+
+template <class TRAITS>
+template <class Visitor>
+/*static*/ bool CrossLoaderAllocatorHash<TRAITS>::VisitTrackerAllEntries(LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe, Visitor &visitor)
+{
+    CONTRACTL
+    {
+        MODE_COOPERATIVE;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    struct
+    {
+        LAHASHDEPENDENTHASHTRACKERREF tracker;
+        OBJECTREF loaderAllocatorRef;
+        GCHEAPHASHOBJECTREF keyToValuesHashObject;
+        KeyToValuesGCHeapHash keyToValuesHash;
+        OBJECTREF keyValueStore;
+    }gc;
+
+    class VisitAllEntryKeyValueHash
+    {
+        public:
+        Visitor *m_pVisitor;
+        KeyToValuesGCHeapHash *m_pKeysToValueHash;
+        OBJECTREF *m_pKeyValueStore;
+        OBJECTREF *m_pLoaderAllocatorRef;
+
+        VisitAllEntryKeyValueHash(Visitor *pVisitor,  KeyToValuesGCHeapHash *pKeysToValueHash, OBJECTREF *pKeyValueStore, OBJECTREF *pLoaderAllocatorRef) : 
+            m_pVisitor(pVisitor),
+            m_pKeysToValueHash(pKeysToValueHash),
+            m_pKeyValueStore(pKeyValueStore),
+            m_pLoaderAllocatorRef(pLoaderAllocatorRef)
+            {}
+
+        bool operator()(INT32 index)
+        {
+            WRAPPER_NO_CONTRACT;
+
+            m_pKeysToValueHash->GetElement(index, *m_pKeyValueStore);
+            return VisitKeyValueStore(m_pLoaderAllocatorRef, m_pKeyValueStore, visitor);
+        }
+    };
+
+    ZeroMemory(&gc, sizeof(gc));
+    gc.tracker = trackerUnsafe;
+
+    bool result = true;
+
+    GCPROTECT_BEGIN(gc);
+    {
+        gc.tracker->GetDependentAndLoaderAllocator(&gc.loaderAllocatorRef, &gc.keyToValuesHashObject);
+        if (gc.keyToValuesHashObject != NULL)
+        {
+            gc.keyToValuesHash = KeyToValuesGCHeapHash(gc.keyToValuesHashObject);
+            result = gc.keyToValuesHash.VisitAllEntryIndices(VisitAllEntryKeyValueHash(&visitor, &gc.keyToValuesHash, &gc.keyValueStore, &gc.loaderAllocatorRef));
+        }
+    }
+    GCPROTECT_END();
+
+    return result;
+}
+
+template <class TRAITS>
+template <class Visitor>
+/*static*/ bool CrossLoaderAllocatorHash<TRAITS>::VisitKeyToTrackerAllEntries(OBJECTREF hashKeyEntryUnsafe, Visitor &visitor)
+{
+    CONTRACTL
+    {
+        MODE_COOPERATIVE;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    struct
+    {
+        OBJECTREF hashKeyEntry;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+        OBJECTREF keyValueStore;
+        OBJECTREF loaderAllocatorRef;
+    } gc;
+
+    ZeroMemory(&gc, sizeof(gc));
+    gc.hashKeyEntry = hashKeyEntryUnsafe;
+
+    bool result = true;
+
+    GCPROTECT_BEGIN(gc);
+    {
+        if (gc.hashKeyEntry->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHKEYTOTRACKERS))
+        {
+            gc.hashKeyToTrackers = (LAHASHKEYTOTRACKERSREF)gc.hashKeyEntry;
+            gc.keyValueStore = gc.hashKeyToTrackers->_laLocalKeyValueStore;
+        }
+        else
+        {
+            gc.keyValueStore = gc.hashKeyEntry;
+        }
+
+        result = VisitKeyValueStore(&gc.loaderAllocatorRef, &gc.keyValueStore, visitor);
+    }
+    GCPROTECT_END();
+
+    return result;
+}
+
+template <class TRAITS>
+/*static*/ void CrossLoaderAllocatorHash<TRAITS>::DeleteEntryTracker(TKey key, LAHASHDEPENDENTHASHTRACKERREF trackerUnsafe)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    struct 
+    {
+        LAHASHDEPENDENTHASHTRACKERREF tracker;
+        OBJECTREF loaderAllocatorRef;
+        GCHEAPHASHOBJECTREF keyToValuesHashObject;
+        KeyToValuesGCHeapHash keyToValuesHash;
+    }gc;
+
+    ZeroMemory(&gc, sizeof(gc));
+    gc.tracker = trackerUnsafe;
+
+    GCPROTECT_BEGIN(gc);
+    {
+        gc.tracker->GetDependentAndLoaderAllocator(&gc.loaderAllocatorRef, &gc.keyToValuesHashObject);
+        if (gc.keyToValuesHashObject != NULL)
+        {
+            gc.keyToValuesHash = KeyToValuesGCHeapHash(gc.keyToValuesHashObject);
+            gc.keyToValuesHash.DeleteEntry(&key);
+        }
+    }
+    GCPROTECT_END();
+}
+
+#ifndef DACCESS_COMPILE
+/*static */inline void CrossLoaderAllocatorHashSetup::EnsureTypesLoaded()
+{
+    STANDARD_VM_CONTRACT;
+
+    // Force these types to be loaded, so that the hashtable logic can use MscorlibBinder::GetExistingClass
+    // throughout and avoid lock ordering issues
+    MscorlibBinder::GetClass(CLASS__LAHASHKEYTOTRACKERS);
+    MscorlibBinder::GetClass(CLASS__LAHASHDEPENDENTHASHTRACKER);
+    MscorlibBinder::GetClass(CLASS__GCHEAPHASH);
+    TypeHandle elemType = TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_I1));
+    TypeHandle typHnd = ClassLoader::LoadArrayTypeThrowing(elemType, ELEMENT_TYPE_SZARRAY, 0);
+    elemType = TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_OBJECT));
+    typHnd = ClassLoader::LoadArrayTypeThrowing(elemType, ELEMENT_TYPE_SZARRAY, 0);
+}
+
+template <class TRAITS>
+void CrossLoaderAllocatorHash<TRAITS>::EnsureManagedObjectsInitted()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    if (m_loaderAllocatorToDependentTrackerHash == NULL)
+    {
+        OBJECTREF laToDependentHandleHashObject = AllocateObject(MscorlibBinder::GetExistingClass(CLASS__GCHEAPHASH));
+        m_loaderAllocatorToDependentTrackerHash = m_pLoaderAllocator->GetDomain()->CreateHandle(laToDependentHandleHashObject);
+        m_pLoaderAllocator->RegisterHandleForCleanup(m_loaderAllocatorToDependentTrackerHash);
+    }
+
+    if (m_keyToDependentTrackersHash == NULL)
+    {
+        OBJECTREF m_keyToDependentTrackersHashObject = AllocateObject(MscorlibBinder::GetExistingClass(CLASS__GCHEAPHASH));
+        m_keyToDependentTrackersHash = m_pLoaderAllocator->GetDomain()->CreateHandle(m_keyToDependentTrackersHashObject);
+        m_pLoaderAllocator->RegisterHandleForCleanup(m_keyToDependentTrackersHash);
+    }
+}
+#endif // !DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+template <class TRAITS>
+LAHASHDEPENDENTHASHTRACKERREF CrossLoaderAllocatorHash<TRAITS>::GetDependentTrackerForLoaderAllocator(LoaderAllocator* pLoaderAllocator)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    struct 
+    {
+        GCHeapHashDependentHashTrackerHash dependentTrackerHash;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+        GCHEAPHASHOBJECTREF GCHeapHashForKeyToValueStore;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    GCPROTECT_BEGIN(gc)
+    {
+        gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash((GCHEAPHASHOBJECTREF)ObjectFromHandle(m_loaderAllocatorToDependentTrackerHash));
+        INT32 index = gc.dependentTrackerHash.GetValueIndex(&pLoaderAllocator);
+        if (index != -1)
+        {
+            // We have an entry in the hashtable for the key/dependenthandle.
+            gc.dependentTrackerHash.GetElement(index, gc.dependentTracker);
+        }
+        else
+        {
+            gc.dependentTracker = (LAHASHDEPENDENTHASHTRACKERREF)AllocateObject(MscorlibBinder::GetExistingClass(CLASS__LAHASHDEPENDENTHASHTRACKER));
+            gc.GCHeapHashForKeyToValueStore = (GCHEAPHASHOBJECTREF)AllocateObject(MscorlibBinder::GetExistingClass(CLASS__GCHEAPHASH));
+            OBJECTHANDLE dependentHandle = GetAppDomain()->CreateDependentHandle(pLoaderAllocator->GetExposedObject(), gc.GCHeapHashForKeyToValueStore);
+            gc.dependentTracker->Init(dependentHandle, pLoaderAllocator);
+            gc.dependentTrackerHash.Add(&pLoaderAllocator, [&gc](PTRARRAYREF arr, INT32 index)
+            {
+                arr->SetAt(index, (OBJECTREF)gc.dependentTracker);
+            });
+        }
+    }
+    GCPROTECT_END();
+
+    return gc.dependentTracker;
+}
+#endif // !DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+template <class TRAITS>
+GCHEAPHASHOBJECTREF CrossLoaderAllocatorHash<TRAITS>::GetKeyToValueCrossLAHashForHashkeyToTrackers(LAHASHKEYTOTRACKERSREF hashKeyToTrackersUnsafe, LoaderAllocator* pValueLoaderAllocator)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    struct 
+    {
+        GCHeapHashDependentHashTrackerHash dependentTrackerHash;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTrackerMaybe;
+        LAHASHDEPENDENTHASHTRACKERREF dependentTracker;
+        LAHASHKEYTOTRACKERSREF hashKeyToTrackers;
+        GCHEAPHASHOBJECTREF returnValue;
+    } gc;
+    ZeroMemory(&gc, sizeof(gc));
+    // Now gc.hashKeyToTrackers is filled in.
+    gc.hashKeyToTrackers = hashKeyToTrackersUnsafe;
+    GCPROTECT_BEGIN(gc)
+    {
+        EnsureManagedObjectsInitted();
+
+        // Is there a single dependenttracker here, or a set, or no dependenttracker at all
+        if (gc.hashKeyToTrackers->_trackerOrTrackerSet == NULL)
+        {
+            gc.dependentTracker = GetDependentTrackerForLoaderAllocator(pValueLoaderAllocator);
+            SetObjectReference(&gc.hashKeyToTrackers->_trackerOrTrackerSet, gc.dependentTracker, GetAppDomain());
+        }
+        else if (gc.hashKeyToTrackers->_trackerOrTrackerSet->GetMethodTable() == MscorlibBinder::GetExistingClass(CLASS__LAHASHDEPENDENTHASHTRACKER))
+        {
+            gc.dependentTrackerMaybe = (LAHASHDEPENDENTHASHTRACKERREF)gc.hashKeyToTrackers->_trackerOrTrackerSet;
+            if (gc.dependentTrackerMaybe->IsTrackerFor(pValueLoaderAllocator))
+            {
+                // We've found the right dependent tracker.
+                gc.dependentTracker = gc.dependentTrackerMaybe;
+            }
+            else
+            {
+                gc.dependentTracker = GetDependentTrackerForLoaderAllocator(pValueLoaderAllocator);
+                if (!gc.dependentTrackerMaybe->IsLoaderAllocatorLive())
+                {
+                    SetObjectReference(&gc.hashKeyToTrackers->_trackerOrTrackerSet, gc.dependentTracker, GetAppDomain());
+                }
+                else
+                {
+                    // Allocate the dependent tracker hash
+                    // Fill with the existing dependentTrackerMaybe, and gc.DependentTracker
+                    gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash(AllocateObject(MscorlibBinder::GetExistingClass(CLASS__GCHEAPHASH)));
+                    LoaderAllocator *pLoaderAllocatorKey = gc.dependentTracker->GetLoaderAllocatorUnsafe();
+                    gc.dependentTrackerHash.Add(&pLoaderAllocatorKey, [&gc](PTRARRAYREF arr, INT32 index)
+                        {
+                            arr->SetAt(index, (OBJECTREF)gc.dependentTracker);
+                        });
+                    pLoaderAllocatorKey = gc.dependentTrackerMaybe->GetLoaderAllocatorUnsafe();
+                    gc.dependentTrackerHash.Add(&pLoaderAllocatorKey, [&gc](PTRARRAYREF arr, INT32 index)
+                        {
+                            arr->SetAt(index, (OBJECTREF)gc.dependentTrackerMaybe);
+                        });
+                    SetObjectReference(&gc.hashKeyToTrackers->_trackerOrTrackerSet, gc.dependentTrackerHash.GetGCHeapRef(), GetAppDomain());
+                }
+            }
+        }
+        else
+        {
+            gc.dependentTrackerHash = GCHeapHashDependentHashTrackerHash(gc.hashKeyToTrackers->_trackerOrTrackerSet);
+
+            INT32 indexOfTracker = gc.dependentTrackerHash.GetValueIndex(&pValueLoaderAllocator);
+            if (indexOfTracker == -1)
+            {
+                // Dependent tracker not yet attached to this key
+                
+                // Get dependent tracker
+                gc.dependentTracker = GetDependentTrackerForLoaderAllocator(pValueLoaderAllocator);
+                gc.dependentTrackerHash.Add(&pValueLoaderAllocator, [&gc](PTRARRAYREF arr, INT32 index)
+                    {
+                        arr->SetAt(index, (OBJECTREF)gc.dependentTracker);
+                    });
+            }
+            else
+            {
+                gc.dependentTrackerHash.GetElement(indexOfTracker, gc.dependentTracker);
+            }
+        }
+
+        // At this stage gc.dependentTracker is setup to have a good value
+        gc.dependentTracker->GetDependentAndLoaderAllocator(NULL, &gc.returnValue);
+    }
+    GCPROTECT_END();
+
+    return gc.returnValue;
+}
+#endif // !DACCESS_COMPILE
+
+#endif // !CROSSGEN_COMPILE
+#endif // CROSSLOADERALLOCATORHASH_H
+#endif // CROSSLOADERALLOCATORHASH_INL
index 8847641..3d092bd 100644 (file)
@@ -3674,4 +3674,6 @@ public:
 #undef FPO_ON
 #endif
 
+#include "crossloaderallocatorhash.inl"
+
 #endif  //__frames_h__
diff --git a/src/vm/gcheaphashtable.h b/src/vm/gcheaphashtable.h
new file mode 100644 (file)
index 0000000..a795ce6
--- /dev/null
@@ -0,0 +1,142 @@
+// 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.
+
+#ifndef GCHEAPHASHTABLE_H
+#define GCHEAPHASHTABLE_H
+
+class GCHeapHashObject;
+
+template <bool removeSupported>
+struct DefaultGCHeapHashTraits
+{
+    typedef PTRARRAYREF THashArrayType;
+    static const INT32 s_growth_factor_numerator = 3;
+    static const INT32 s_growth_factor_denominator = 2;
+
+    static const INT32 s_density_factor_numerator = 3;
+    static const INT32 s_density_factor_denominator = 4;
+
+    static const INT32 s_densitywithdeletes_factor_numerator = 7;
+    static const INT32 s_densitywithdeletes_factor_denominator = 8;
+
+    static const INT32 s_minimum_allocation = 7;
+
+    static bool IsNull(PTRARRAYREF arr, INT32 index);
+    static bool IsDeleted(PTRARRAYREF arr, INT32 index, GCHEAPHASHOBJECTREF gcHeap);
+#ifndef DACCESS_COMPILE
+    static THashArrayType AllocateArray(INT32 size);
+#endif
+
+    // Not a part of the traits api, but used to allow derived traits to save on code
+    static OBJECTREF GetValueAtIndex(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index);
+
+#ifndef DACCESS_COMPILE
+    static void CopyValue(THashArrayType srcArray, INT32 indexSrc, THashArrayType destinationArray, INT32 indexDest);
+    static void DeleteEntry(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index);
+#endif // !DACCESS_COMPILE
+
+    template<class TElement>
+    static void GetElement(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index, TElement& foundElement);
+
+#ifndef DACCESS_COMPILE
+    template<class TElement>
+    static void SetElement(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index, TElement& foundElement);
+#endif // !DACCESS_COMPILE
+};
+
+template <class PtrTypeKey, bool supports_remove>
+struct GCHeapHashTraitsPointerToPointerList : public DefaultGCHeapHashTraits<supports_remove>
+{
+    static INT32 Hash(PtrTypeKey *pValue);
+    static INT32 Hash(PTRARRAYREF arr, INT32 index);
+    static bool DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, PtrTypeKey *pKey);
+};
+
+
+// GCHeapHash is based on the logic of SHash, and utilizes the same basic structure (which allows the key/value 
+// to be one and the same, or other interesting memory tweaks.) To avoid GC pointer issues, responsibility for allocating
+// the underlying arrays and manipulating the entries is entirely extracted to the traits class, and responsibility
+// for creation of elements is deferred into the caller of the add function. (See example uses in CrossLoaderAllocatorHash)
+// As the GCHeapHash is actually a managed object, but the code for manipulating the hash is written here in native code, 
+// allocating an instance of this class does not actually allocate a hashtable. Instead, the hashtable is allocated by
+// allocating an instance of the GCHeapHash type, and then passing the allocated object into this type's constructor to 
+// assign the value. This class is designed to be used protected within a GC_PROTECT region. See examples in CrossLoaderAllocatorHash.
+template <class TRAITS>
+class GCHeapHash
+{
+    GCHEAPHASHOBJECTREF m_gcHeapHash;
+
+    typedef typename TRAITS::THashArrayType THashArrayType;
+    typedef INT32 count_t;
+
+    private:
+    // Insert into hashtable without growing. GCHEAPHASHOBJECTREF must be GC protected as must be TKey if needed
+    template<class TKey, class TValueSetter>
+    void Insert(TKey *pKey, const TValueSetter &valueSetter);
+    void CheckGrowth();
+    void Grow();
+    THashArrayType Grow_OnlyAllocateNewTable();
+
+    bool IsPrime(count_t number);
+    count_t NextPrime(count_t number);
+
+    void ReplaceTable(THashArrayType newTable);
+
+    template<class TKey>
+    count_t CallHash(TKey* pValue)
+    {
+        WRAPPER_NO_CONTRACT;
+
+        count_t hash = TRAITS::Hash(pValue);
+        hash = hash < 0 ? -hash : hash;
+        if (hash < 0)
+            return 1;
+        else
+            return hash;
+    }
+
+    count_t CallHash(THashArrayType arr, count_t index)
+    {
+        WRAPPER_NO_CONTRACT;
+
+        count_t hash = TRAITS::Hash(arr, index);
+        hash = hash < 0 ? -hash : hash;
+        if (hash < 0)
+            return 1;
+        else
+            return hash;
+    }
+
+    public:
+
+    template<class TVisitor>
+    bool VisitAllEntryIndices(TVisitor &visitor);
+
+    template<class TKey, class TValueSetter>
+    void Add(TKey *pKey, const TValueSetter &valueSetter);
+
+    // Get the index in the hashtable of the value which matches key, or -1 if there are no matches
+    template<class TKey>
+    INT32 GetValueIndex(TKey *pKey);
+
+    template<class TElement>
+    void GetElement(INT32 index, TElement& foundElement);
+
+    // Use this to update an value within the hashtable directly. 
+    // It is ONLY safe to do if the index already points at an element
+    // which already exists and has the same key as the newElementValue
+    template<class TElement>
+    void SetElement(INT32 index, TElement& newElementValue);
+
+    template<class TKey>
+    void DeleteEntry(TKey *pKey);
+
+    GCHEAPHASHOBJECTREF GetGCHeapRef() { LIMITED_METHOD_CONTRACT; return m_gcHeapHash; }
+
+    GCHeapHash(GCHEAPHASHOBJECTREF gcHeap) : m_gcHeapHash(gcHeap) {}
+    GCHeapHash(OBJECTREF gcHeap) : m_gcHeapHash((GCHEAPHASHOBJECTREF)gcHeap) {}
+    GCHeapHash() : m_gcHeapHash((GCHEAPHASHOBJECTREF)TADDR(NULL)) {}
+};
+
+#endif // GCHEAPHASHTABLE_H
diff --git a/src/vm/gcheaphashtable.inl b/src/vm/gcheaphashtable.inl
new file mode 100644 (file)
index 0000000..f2256be
--- /dev/null
@@ -0,0 +1,530 @@
+// 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.
+
+#ifdef GCHEAPHASHTABLE_H
+#ifndef GCHEAPHASHTABLE_INL
+#define GCHEAPHASHTABLE_INL
+
+template <bool removeSupported>
+/*static */bool DefaultGCHeapHashTraits<removeSupported>::IsNull(PTRARRAYREF arr, INT32 index)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return arr->GetAt(index) == 0;
+}
+
+template <bool removeSupported>
+/*static */bool DefaultGCHeapHashTraits<removeSupported>::IsDeleted(PTRARRAYREF arr, INT32 index, GCHEAPHASHOBJECTREF gcHeap)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    if (removeSupported)
+    {
+        return gcHeap == arr->GetAt(index);
+    }
+    else
+    {
+        return false;
+    }
+}
+
+#ifndef DACCESS_COMPILE
+template <bool removeSupported>
+/*static*/ typename DefaultGCHeapHashTraits<removeSupported>::THashArrayType DefaultGCHeapHashTraits<removeSupported>::AllocateArray(INT32 size)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    return (THashArrayType)AllocateObjectArray(size, g_pObjectClass);
+}
+#endif // !DACCESS_COMPILE
+
+    // Not a part of the traits api, but used to allow derived traits to save on code
+template <bool removeSupported>
+/*static*/ OBJECTREF DefaultGCHeapHashTraits<removeSupported>::GetValueAtIndex(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    PTRARRAYREF arr((PTRARRAYREF)(*pgcHeap)->GetData());
+
+    OBJECTREF value = arr->GetAt(index);
+
+    return value;
+}
+
+#ifndef DACCESS_COMPILE
+template <bool removeSupported>
+/*static*/ void DefaultGCHeapHashTraits<removeSupported>::CopyValue(THashArrayType srcArray, INT32 indexSrc, THashArrayType destinationArray, INT32 indexDest)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    if (srcArray == NULL)
+        COMPlusThrow(kNullReferenceException);
+
+    if ((INT32)srcArray->GetNumComponents() < indexSrc)
+        COMPlusThrow(kIndexOutOfRangeException);
+
+    OBJECTREF value = srcArray->GetAt(indexSrc);
+
+    if ((INT32)destinationArray->GetNumComponents() < indexDest)
+        COMPlusThrow(kIndexOutOfRangeException);
+
+    destinationArray->SetAt(indexDest, value);
+}
+#endif // !DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+template <bool removeSupported>
+/*static*/ void DefaultGCHeapHashTraits<removeSupported>::DeleteEntry(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    static_assert(removeSupported, "This hash doesn't support remove");
+
+    PTRARRAYREF arr((PTRARRAYREF)(*pgcHeap)->GetData());
+
+    if (arr == NULL)
+        COMPlusThrow(kNullReferenceException);
+
+    if ((INT32)arr->GetNumComponents() < index)
+        COMPlusThrow(kIndexOutOfRangeException);
+
+    // The deleted sentinel is a self-pointer
+    arr->SetAt(index, *pgcHeap);
+}
+#endif // !DACCESS_COMPILE
+
+template <bool removeSupported>
+template<class TElement>
+/*static*/ void DefaultGCHeapHashTraits<removeSupported>::GetElement(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index, TElement& foundElement)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    foundElement = (TElement)GetValueAtIndex(pgcHeap, index);
+}
+
+#ifndef DACCESS_COMPILE
+template <bool removeSupported>
+template<class TElement>
+/*static*/ void DefaultGCHeapHashTraits<removeSupported>::SetElement(GCHEAPHASHOBJECTREF *pgcHeap, INT32 index, TElement& foundElement)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    PTRARRAYREF arr((PTRARRAYREF)(*pgcHeap)->GetData());
+
+    if (arr == NULL)
+        COMPlusThrow(kNullReferenceException);
+
+    if ((INT32)arr->GetNumComponents() < index)
+        COMPlusThrow(kIndexOutOfRangeException);
+
+    arr->SetAt(index, foundElement);
+}
+#endif // !DACCESS_COMPILE
+
+template <class PtrTypeKey, bool supports_remove>
+/*static */INT32 GCHeapHashTraitsPointerToPointerList<PtrTypeKey, supports_remove>::Hash(PtrTypeKey *pValue)
+{
+    LIMITED_METHOD_CONTRACT;
+    return (INT32)*pValue;
+}
+
+template <class PtrTypeKey, bool supports_remove>
+/*static */INT32 GCHeapHashTraitsPointerToPointerList<PtrTypeKey, supports_remove>::Hash(PTRARRAYREF arr, INT32 index)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    UPTRARRAYREF value = (UPTRARRAYREF)arr->GetAt(index);
+    
+    return (INT32)*value->GetDirectConstPointerToNonObjectElements();
+}
+
+template <class PtrTypeKey, bool supports_remove>
+/*static */bool GCHeapHashTraitsPointerToPointerList<PtrTypeKey, supports_remove>::DoesEntryMatchKey(PTRARRAYREF arr, INT32 index, PtrTypeKey *pKey)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    UPTRARRAYREF value = (UPTRARRAYREF)arr->GetAt(index);
+    UPTR uptrValue = *value->GetDirectConstPointerToNonObjectElements();
+
+    return ((UPTR)*pKey) == uptrValue;
+}
+
+template <class TRAITS>
+template<class TKey, class TValueSetter>
+void GCHeapHash<TRAITS>::Insert(TKey *pKey, const TValueSetter &valueSetter)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    count_t hash = CallHash(pKey);
+    count_t tableSize = m_gcHeapHash->GetCapacity();
+    count_t index = hash % tableSize; 
+    count_t increment = 0; // delay computation
+
+    while (TRUE)
+    {
+        THashArrayType arr((THashArrayType)(m_gcHeapHash)->GetData());
+
+        bool isNull = TRAITS::IsNull(arr, index);
+        bool isDeleted = false;
+        if (!isNull && TRAITS::IsDeleted(arr, index, m_gcHeapHash))
+            isDeleted = true;
+
+        if (isNull || isDeleted)
+        {
+            if (arr == NULL)
+                COMPlusThrow(kNullReferenceException);
+
+            if ((INT32)arr->GetNumComponents() < index)
+                COMPlusThrow(kIndexOutOfRangeException);
+
+            valueSetter(arr, index);
+            m_gcHeapHash->IncrementCount(isDeleted);
+            return;
+        }
+    
+        if (increment == 0)
+            increment = (hash % (tableSize-1)) + 1; 
+    
+        index += increment;
+        if (index >= tableSize)
+            index -= tableSize;
+    }
+}
+
+template <class TRAITS>
+void GCHeapHash<TRAITS>::CheckGrowth()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    count_t tableMax = (count_t) (m_gcHeapHash->GetCapacity() * TRAITS::s_density_factor_numerator / TRAITS::s_density_factor_denominator);
+    if (m_gcHeapHash->GetCount() == tableMax)
+        Grow();
+    else
+    {
+        tableMax = (count_t) (m_gcHeapHash->GetCapacity() * TRAITS::s_densitywithdeletes_factor_numerator / TRAITS::s_densitywithdeletes_factor_denominator);
+        if ((m_gcHeapHash->GetCount() + m_gcHeapHash->GetDeletedCount()) >= tableMax)
+        {
+            THashArrayType newTable = TRAITS::AllocateArray(m_gcHeapHash->GetCapacity());
+            ReplaceTable(newTable);
+        }
+    }
+}
+
+template <class TRAITS>
+void GCHeapHash<TRAITS>::Grow()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    THashArrayType newTable = Grow_OnlyAllocateNewTable();
+    ReplaceTable(newTable);
+}
+
+template <class TRAITS>
+typename GCHeapHash<TRAITS>::THashArrayType GCHeapHash<TRAITS>::Grow_OnlyAllocateNewTable()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        INSTANCE_CHECK;
+    }
+    CONTRACTL_END;
+
+    count_t newSize = (count_t) (m_gcHeapHash->GetCount()
+                                * TRAITS::s_growth_factor_numerator / TRAITS::s_growth_factor_denominator
+                                * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator);
+    if (newSize < TRAITS::s_minimum_allocation)
+        newSize = TRAITS::s_minimum_allocation;
+
+    // handle potential overflow
+    if (newSize < m_gcHeapHash->GetCount())
+        ThrowOutOfMemory();
+
+    return TRAITS::AllocateArray(NextPrime(newSize));
+}
+
+template <class TRAITS>
+bool GCHeapHash<TRAITS>::IsPrime(count_t number)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    // This is a very low-tech check for primality, which doesn't scale very well.  
+    // There are more efficient tests if this proves to be burdensome for larger
+    // tables.
+
+    if ((number & 1) == 0)
+        return false;
+
+    count_t factor = 3;
+    while (factor * factor <= number)
+    {
+        if ((number % factor) == 0)
+            return false;
+        factor += 2;
+    }
+
+    return true;
+}
+
+template <class TRAITS>
+typename GCHeapHash<TRAITS>::count_t GCHeapHash<TRAITS>::NextPrime(count_t number)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    static_assert(sizeof(INT32) == sizeof(g_shash_primes[0]), "the cast below of g_shash_primes[] to INT32 isn't safe due to loss of precision.");
+
+    for (int i = 0; i < (int) (sizeof(g_shash_primes) / sizeof(g_shash_primes[0])); i++) {
+        if ((INT32)g_shash_primes[i] >= number)
+            return (INT32)g_shash_primes[i];
+    }
+
+    if ((number&1) == 0)
+        number++;
+
+    while (number != 1) {
+        if (IsPrime(number))
+            return number;
+        number +=2;
+    }
+
+    // overflow
+    ThrowOutOfMemory();
+}
+
+template <class TRAITS>
+void GCHeapHash<TRAITS>::ReplaceTable(THashArrayType newTable)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    GCPROTECT_BEGIN(newTable);
+    {
+        count_t newTableSize = (count_t)newTable->GetNumComponents();
+        count_t oldTableSize = m_gcHeapHash->GetCapacity();
+
+        // Move all entries over to the new table
+        count_t capacity = m_gcHeapHash->GetCapacity();
+
+        for (count_t index = 0; index < capacity; ++index)
+        {
+            THashArrayType arr((THashArrayType)(m_gcHeapHash)->GetData());
+            if (!TRAITS::IsNull(arr, index) && !TRAITS::IsDeleted(arr, index, m_gcHeapHash))
+            {
+                count_t hash = CallHash(arr, index);
+                count_t tableSize = (count_t)newTable->GetNumComponents();
+                count_t newIndex = hash % tableSize; 
+                count_t increment = 0; // delay computation
+
+                // Value to copy is in index
+                while (TRUE)
+                {
+                    if (TRAITS::IsNull(newTable, newIndex))
+                    {
+                        arr = (THashArrayType)(m_gcHeapHash)->GetData();
+                        TRAITS::CopyValue(arr, index, newTable, newIndex);
+                        break;
+                    }
+                
+                    if (increment == 0)
+                        increment = (hash % (tableSize-1)) + 1; 
+                
+                    newIndex += increment;
+                    if (newIndex >= tableSize)
+                        newIndex -= tableSize;
+                }
+            }
+        }
+
+        m_gcHeapHash->SetTable((BASEARRAYREF)newTable);
+        
+        // We've just copied the table to a new table. There are no deleted items as
+        // we skipped them all, so reset the deleted count to zero
+        m_gcHeapHash->SetDeletedCountToZero(); 
+    }
+    GCPROTECT_END();
+}
+
+template <class TRAITS>
+template<class TVisitor>
+bool GCHeapHash<TRAITS>::VisitAllEntryIndices(TVisitor &visitor)
+{
+    WRAPPER_NO_CONTRACT;
+
+    count_t capacity = m_gcHeapHash->GetCapacity();
+
+    for (count_t index = 0; index < capacity; ++index)
+    {
+        THashArrayType arr((THashArrayType)(m_gcHeapHash)->GetData());
+        if (!TRAITS::IsNull(arr, index) && !TRAITS::IsDeleted(arr, index, m_gcHeapHash))
+        {
+            if (!visitor(index))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+template <class TRAITS>
+template<class TKey, class TValueSetter>
+void GCHeapHash<TRAITS>::Add(TKey *pKey, const TValueSetter &valueSetter)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    CheckGrowth();
+    Insert(pKey, valueSetter);
+}
+
+    // Get the index in the hashtable of the value which matches key, or -1 if there are no matches
+template <class TRAITS>
+template<class TKey>
+INT32 GCHeapHash<TRAITS>::GetValueIndex(TKey *pKey)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    count_t hash = CallHash(pKey);
+    count_t tableSize = m_gcHeapHash->GetCapacity();
+
+    // If the table is empty, then there aren't any entries. Just return.
+    if (m_gcHeapHash->GetCount() == 0)
+        return -1;
+
+    count_t index = hash % tableSize; 
+    count_t increment = 0; // delay computation
+
+    THashArrayType arr((THashArrayType)(m_gcHeapHash)->GetData());
+
+    while (m_gcHeapHash->GetCount() != 0) /* the TRAITS::IsDeleted function is allowed to reduce the count */
+    {
+        if (TRAITS::IsNull(arr, index))
+        {
+            return -1;
+        }
+
+        if (!TRAITS::IsDeleted(arr, index, m_gcHeapHash) && TRAITS::DoesEntryMatchKey(arr, index, pKey))
+        {
+            return index;
+        }
+    
+        if (increment == 0)
+            increment = (hash % (tableSize-1)) + 1; 
+
+        index += increment;
+        if (index >= tableSize)
+            index -= tableSize;
+    }
+
+    return -1;
+}
+
+template <class TRAITS>
+template<class TElement>
+void GCHeapHash<TRAITS>::GetElement(INT32 index, TElement& foundElement)
+{
+    WRAPPER_NO_CONTRACT;
+
+    TRAITS::GetElement(&m_gcHeapHash, index, foundElement);
+}
+
+    // Use this to update an value within the hashtable directly. 
+    // It is ONLY safe to do if the index already points at an element
+    // which already exists and has the same key as the newElementValue
+template <class TRAITS>
+template<class TElement>
+void GCHeapHash<TRAITS>::SetElement(INT32 index, TElement& newElementValue)
+{
+    TRAITS::SetElement(&m_gcHeapHash, index, newElementValue);
+}
+
+template <class TRAITS>
+template<class TKey>
+void GCHeapHash<TRAITS>::DeleteEntry(TKey *pKey)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+    }
+    CONTRACTL_END;
+
+    INT32 index = GetValueIndex(pKey);
+    if (index != -1)
+    {
+        TRAITS::DeleteEntry(&m_gcHeapHash, index);
+        m_gcHeapHash->DecrementCount(true);
+    }
+}
+
+#endif // GCHEAPHASHTABLE_INL
+#endif // GCHEAPHASHTABLE_H
index a54b093..ae57e1f 100644 (file)
@@ -94,9 +94,6 @@ LoaderAllocator::~LoaderAllocator()
 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
     Terminate();
 
-    // This info is cleaned up before the virtual call stub manager is uninitialized
-    _ASSERTE(!GetMethodDescBackpatchInfoTracker()->HasDependencyMethodDescEntryPointSlots());
-
     // Assert that VSD is not still active when the destructor is called.
     _ASSERTE(m_pVirtualCallStubManager == NULL);
 
@@ -595,11 +592,6 @@ void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocat
 
         pDomainLoaderAllocatorDestroyIterator->ReleaseManagedAssemblyLoadContext();
 
-        // Recorded entry point slots may point into the virtual call stub manager's heaps, so clear it first
-        pDomainLoaderAllocatorDestroyIterator
-            ->GetMethodDescBackpatchInfoTracker()
-            ->ClearDependencyMethodDescEntryPointSlots(pDomainLoaderAllocatorDestroyIterator);
-
         // The following code was previously happening on delete ~DomainAssembly->Terminate
         // We are moving this part here in order to make sure that we can unload a LoaderAllocator
         // that didn't have a DomainAssembly
@@ -1080,6 +1072,10 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory)
     m_ComCallWrapperCrst.Init(CrstCOMCallWrapper);
 #endif
 
+#ifndef CROSSGEN_COMPILE
+    m_methodDescBackpatchInfoTracker.Initialize(this);
+#endif
+
     //
     // Initialize the heaps
     //
index dc63ab9..100c0d7 100644 (file)
@@ -22,6 +22,7 @@ class FuncPtrStubs;
 
 #include "callcounter.h"
 #include "methoddescbackpatchinfo.h"
+#include "crossloaderallocatorhash.h"
 
 #define VPTRU_LoaderAllocator 0x3200
 
@@ -32,6 +33,8 @@ enum LoaderAllocatorType
     LAT_Assembly
 };
 
+typedef SHash<PtrSetSHashTraits<LoaderAllocator *>> LoaderAllocatorSet;
+
 class CLRPrivBinderAssemblyLoadContext;
 
 // Iterator over a DomainAssembly in the same ALC
index 69849f3..70d79ca 100644 (file)
@@ -4864,25 +4864,8 @@ void MethodDesc::RecordAndBackpatchEntryPointSlot_Locked(
     // current, entry point until another entry point change, which may never happen.
     _ASSERTE(currentEntryPoint == GetEntryPointToBackpatch_Locked());
 
-    MethodDescBackpatchInfo *backpatchInfo =
-        mdLoaderAllocator->GetMethodDescBackpatchInfoTracker()->GetOrAddBackpatchInfo_Locked(this);
-    if (slotLoaderAllocator == mdLoaderAllocator)
-    {
-        // Entry point slots to backpatch are recorded in the backpatch info
-        backpatchInfo->GetSlots()->AddSlot_Locked(slot, slotType);
-    }
-    else
-    {
-        // Register the slot's loader allocator with the MethodDesc's backpatch info. Entry point slots to backpatch are
-        // recorded in the slot's LoaderAllocator.
-        backpatchInfo->AddDependentLoaderAllocator_Locked(slotLoaderAllocator);
-        slotLoaderAllocator
-            ->GetMethodDescBackpatchInfoTracker()
-            ->GetOrAddDependencyMethodDescEntryPointSlots_Locked(this)
-            ->AddSlot_Locked(slot, slotType);
-    }
-
-    EntryPointSlots::Backpatch_Locked(slot, slotType, currentEntryPoint);
+    MethodDescBackpatchInfoTracker *backpatchTracker = mdLoaderAllocator->GetMethodDescBackpatchInfoTracker();
+    backpatchTracker->AddSlotAndPatch_Locked(this, slotLoaderAllocator, slot, slotType, currentEntryPoint);
 }
 
 void MethodDesc::BackpatchEntryPointSlots(PCODE entryPoint, bool isPrestubEntryPoint)
@@ -4891,10 +4874,10 @@ void MethodDesc::BackpatchEntryPointSlots(PCODE entryPoint, bool isPrestubEntryP
     _ASSERTE(entryPoint != NULL);
     _ASSERTE(MayHaveEntryPointSlotsToBackpatch());
     _ASSERTE(isPrestubEntryPoint == (entryPoint == GetPrestubEntryPointToBackpatch()));
+    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
 
     LoaderAllocator *mdLoaderAllocator = GetLoaderAllocator();
     MethodDescBackpatchInfoTracker *backpatchInfoTracker = mdLoaderAllocator->GetMethodDescBackpatchInfoTracker();
-    MethodDescBackpatchInfoTracker::ConditionalLockHolder lockHolder;
 
     // Get the entry point to backpatch inside the lock to synchronize with backpatching in MethodDesc::DoBackpatch()
     if (GetEntryPointToBackpatch_Locked() == entryPoint)
@@ -4923,29 +4906,7 @@ void MethodDesc::BackpatchEntryPointSlots(PCODE entryPoint, bool isPrestubEntryP
         }
     }
 
-    MethodDescBackpatchInfo *backpatchInfo = backpatchInfoTracker->GetBackpatchInfo_Locked(this);
-    if (backpatchInfo != nullptr)
-    {
-        // Backpatch slots from the same loader allocator
-        backpatchInfo->GetSlots()->Backpatch_Locked(entryPoint);
-
-        // Backpatch slots from dependent loader allocators
-        backpatchInfo->ForEachDependentLoaderAllocator_Locked(
-            [&](LoaderAllocator *slotLoaderAllocator) // the loader allocator from which the slot's memory is allocated
-        {
-            _ASSERTE(slotLoaderAllocator != nullptr);
-            _ASSERTE(slotLoaderAllocator != mdLoaderAllocator);
-
-            EntryPointSlots *slotsToBackpatch =
-                slotLoaderAllocator
-                ->GetMethodDescBackpatchInfoTracker()
-                ->GetDependencyMethodDescEntryPointSlots_Locked(this);
-            if (slotsToBackpatch != nullptr)
-            {
-                slotsToBackpatch->Backpatch_Locked(entryPoint);
-            }
-        });
-    }
+    backpatchInfoTracker->Backpatch_Locked(this, entryPoint);
 
     // Set the entry point to backpatch inside the lock to synchronize with backpatching in MethodDesc::DoBackpatch(), and set
     // it last in case there are exceptions above, as setting the entry point indicates that all recorded slots have been
index 386786c..571007c 100644 (file)
 
 #ifndef DACCESS_COMPILE
 
-void EntryPointSlots::Backpatch_Locked(PCODE entryPoint)
-{
-    WRAPPER_NO_CONTRACT;
-    static_assert_no_msg(SlotType_Count <= sizeof(INT32));
-    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
-    _ASSERTE(entryPoint != NULL);
-
-    TADDR *slots = m_slots.GetElements();
-    COUNT_T slotCount = m_slots.GetCount();
-    for (COUNT_T i = 0; i < slotCount; ++i)
-    {
-        TADDR slot = slots[i];
-        SlotType slotType = (SlotType)(slot & SlotType_Mask);
-        slot ^= slotType;
-        Backpatch_Locked(slot, slotType, entryPoint);
-    }
-}
-
 void EntryPointSlots::Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entryPoint)
 {
     WRAPPER_NO_CONTRACT;
@@ -81,55 +63,50 @@ void EntryPointSlots::Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entr
 #endif // !DACCESS_COMPILE
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// MethodDescBackpatchInfo
+// MethodDescBackpatchInfoTracker
+
+CrstStatic MethodDescBackpatchInfoTracker::s_lock;
 
 #ifndef DACCESS_COMPILE
 
-void MethodDescBackpatchInfo::AddDependentLoaderAllocator_Locked(LoaderAllocator *dependentLoaderAllocator)
+void MethodDescBackpatchInfoTracker::Backpatch_Locked(MethodDesc *pMethodDesc, PCODE entryPoint)
 {
     WRAPPER_NO_CONTRACT;
-    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
-    _ASSERTE(m_methodDesc != nullptr);
-    _ASSERTE(dependentLoaderAllocator != nullptr);
-    _ASSERTE(dependentLoaderAllocator != m_methodDesc->GetLoaderAllocator());
+    _ASSERTE(IsLockedByCurrentThread());
+    _ASSERTE(pMethodDesc != nullptr);
 
-    LoaderAllocatorSet *set = m_dependentLoaderAllocators;
-    if (set != nullptr)
+    GCX_COOP();
+
+    auto lambda = [&entryPoint](OBJECTREF obj, MethodDesc *pMethodDesc, UINT_PTR slotData)
     {
-        if (set->Lookup(dependentLoaderAllocator) != nullptr)
-        {
-            return;
-        }
-        set->Add(dependentLoaderAllocator);
-        return;
-    }
 
-    NewHolder<LoaderAllocatorSet> setHolder = new LoaderAllocatorSet();
-    setHolder->Add(dependentLoaderAllocator);
-    m_dependentLoaderAllocators = setHolder.Extract();
-}
+        TADDR slot;
+        EntryPointSlots::SlotType slotType;
 
-void MethodDescBackpatchInfo::RemoveDependentLoaderAllocator_Locked(LoaderAllocator *dependentLoaderAllocator)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
-    _ASSERTE(m_methodDesc != nullptr);
-    _ASSERTE(dependentLoaderAllocator != nullptr);
-    _ASSERTE(dependentLoaderAllocator != m_methodDesc->GetLoaderAllocator());
-    _ASSERTE(m_dependentLoaderAllocators != nullptr);
-    _ASSERTE(m_dependentLoaderAllocators->Lookup(dependentLoaderAllocator) == dependentLoaderAllocator);
+        EntryPointSlots::ConvertUINT_PTRToSlotAndTypePair(slotData, &slot, &slotType);
+        EntryPointSlots::Backpatch_Locked(slot, slotType, entryPoint);
 
-    m_dependentLoaderAllocators->Remove(dependentLoaderAllocator);
+        return true; // Keep walking
+    };
+
+    m_backpatchInfoHash.VisitValuesOfKey(pMethodDesc, lambda);
 }
 
-#endif // !DACCESS_COMPILE
+void MethodDescBackpatchInfoTracker::AddSlotAndPatch_Locked(MethodDesc *pMethodDesc, LoaderAllocator *pLoaderAllocatorOfSlot, TADDR slot, EntryPointSlots::SlotType slotType, PCODE currentEntryPoint)
+{
+    WRAPPER_NO_CONTRACT;
+    _ASSERTE(IsLockedByCurrentThread());
+    _ASSERTE(pMethodDesc != nullptr);
+    _ASSERTE(pMethodDesc->MayHaveEntryPointSlotsToBackpatch());
 
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// MethodDescBackpatchInfoTracker
+    GCX_COOP();
 
-CrstStatic MethodDescBackpatchInfoTracker::s_lock;
+    UINT_PTR slotData;
+    slotData = EntryPointSlots::ConvertSlotAndTypePairToUINT_PTR(slot, slotType);
 
-#ifndef DACCESS_COMPILE
+    m_backpatchInfoHash.Add(pMethodDesc, slotData, pLoaderAllocatorOfSlot);
+    EntryPointSlots::Backpatch_Locked(slot, slotType, currentEntryPoint);
+}
 
 void MethodDescBackpatchInfoTracker::StaticInitialize()
 {
@@ -163,76 +140,4 @@ bool MethodDescBackpatchInfoTracker::MayHaveEntryPointSlotsToBackpatch(PTR_Metho
 
 #endif // _DEBUG
 
-#ifndef DACCESS_COMPILE
-
-MethodDescBackpatchInfo *MethodDescBackpatchInfoTracker::AddBackpatchInfo_Locked(MethodDesc *methodDesc)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(IsLockedByCurrentThread());
-    _ASSERTE(methodDesc != nullptr);
-    _ASSERTE(methodDesc->MayHaveEntryPointSlotsToBackpatch());
-    _ASSERTE(m_backpatchInfoHash.Lookup(methodDesc) == nullptr);
-
-    NewHolder<MethodDescBackpatchInfo> backpatchInfoHolder = new MethodDescBackpatchInfo(methodDesc);
-    m_backpatchInfoHash.Add(backpatchInfoHolder);
-    return backpatchInfoHolder.Extract();
-}
-
-EntryPointSlots *MethodDescBackpatchInfoTracker::GetDependencyMethodDescEntryPointSlots_Locked(MethodDesc *methodDesc)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(IsLockedByCurrentThread());
-    _ASSERTE(methodDesc != nullptr);
-    _ASSERTE(methodDesc->MayHaveEntryPointSlotsToBackpatch());
-
-    MethodDescEntryPointSlots *methodDescSlots =
-        m_dependencyMethodDescEntryPointSlotsHash.Lookup(methodDesc);
-    return methodDescSlots == nullptr ? nullptr : methodDescSlots->GetSlots();
-}
-
-EntryPointSlots *MethodDescBackpatchInfoTracker::GetOrAddDependencyMethodDescEntryPointSlots_Locked(MethodDesc *methodDesc)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(IsLockedByCurrentThread());
-    _ASSERTE(methodDesc != nullptr);
-    _ASSERTE(methodDesc->MayHaveEntryPointSlotsToBackpatch());
-
-    MethodDescEntryPointSlots *methodDescSlots = m_dependencyMethodDescEntryPointSlotsHash.Lookup(methodDesc);
-    if (methodDescSlots != nullptr)
-    {
-        return methodDescSlots->GetSlots();
-    }
-
-    NewHolder<MethodDescEntryPointSlots> methodDescSlotsHolder = new MethodDescEntryPointSlots(methodDesc);
-    m_dependencyMethodDescEntryPointSlotsHash.Add(methodDescSlotsHolder);
-    return methodDescSlotsHolder.Extract()->GetSlots();
-}
-
-void MethodDescBackpatchInfoTracker::ClearDependencyMethodDescEntryPointSlots(LoaderAllocator *loaderAllocator)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(loaderAllocator != nullptr);
-    _ASSERTE(loaderAllocator->GetMethodDescBackpatchInfoTracker() == this);
-
-    ConditionalLockHolder lockHolder;
-
-    for (MethodDescEntryPointSlotsHash::Iterator
-            it = m_dependencyMethodDescEntryPointSlotsHash.Begin(),
-            itEnd = m_dependencyMethodDescEntryPointSlotsHash.End();
-        it != itEnd;
-        ++it)
-    {
-        MethodDesc *methodDesc = (*it)->GetMethodDesc();
-        MethodDescBackpatchInfo *backpatchInfo = methodDesc->GetBackpatchInfoTracker()->GetBackpatchInfo_Locked(methodDesc);
-        if (backpatchInfo != nullptr)
-        {
-            backpatchInfo->RemoveDependentLoaderAllocator_Locked(loaderAllocator);
-        }
-    }
-
-    m_dependencyMethodDescEntryPointSlotsHash.RemoveAll();
-}
-
-#endif // DACCESS_COMPILE
-
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
index 3aa2b13..c5d92a2 100644 (file)
@@ -5,29 +5,7 @@
 #pragma once
 
 #include "debugmacrosext.h"
-
-// MethodDescBackpatchInfoTracker:
-//   - Root container for all other types in this file
-//   - There is one instance per LoaderAllocator
-//   - Contains a collection of MethodDescBackpatchInfo objects
-//   - Contains a collection of MethodDescEntryPointSlots objects
-//
-// MethodDescBackpatchInfo:
-//   - Container for backpatch information for a MethodDesc allocated in the same LoaderAllocator
-//   - Contains an EntryPointSlots collection that contains slots allocated in the same LoaderAllocator. These are slots
-//     recorded for backpatching when the MethodDesc's code entry point changes.
-//   - Contains a LoaderAllocatorSet collection that contains dependent LoaderAllocators that in turn have slots recorded for
-//     backpatching when the MethodDesc's entry point changes. These are slots associated with the MethodDesc but allocated and
-//     recorded in a LoaderAllocator on the MethodDesc's LoaderAllocator.
-//
-// EntryPointSlots and MethodDescEntryPointSlots
-//   - Collection of slots recorded for backpatching
-//   - There is one instance per MethodDescBackpatchInfo for slots allocated in the MethodDesc's LoaderAllocator
-//   - There is one instance per MethodDesc in MethodDescBackpatchInfoTracker, for slots allocated in LoaderAllocators that are 
-//     dependent on the MethodDesc's LoaderAllocator. The dependent LoaderAllocators are also recorded in the
-//     MethodDescBackPatchInfo associated with the MethodDesc's LoaderAllocator.
-
-typedef SHash<PtrSetSHashTraits<LoaderAllocator *>> LoaderAllocatorSet;
+#include "crossloaderallocatorhash.h"
 
 #ifndef CROSSGEN_COMPILE
 
@@ -38,7 +16,6 @@ typedef SHash<PtrSetSHashTraits<LoaderAllocator *>> LoaderAllocatorSet;
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // EntryPointSlots
 
-// See comment at the top of methoddescbackpatchinfo.h for a description of this and related data structures
 class EntryPointSlots
 {
 public:
@@ -53,18 +30,6 @@ public:
         SlotType_Mask = SlotType_Vtable | SlotType_Executable | SlotType_ExecutableRel32
     };
 
-private:
-    typedef SArray<TADDR> SlotArray;
-
-private:
-    SlotArray m_slots;
-
-public:
-    EntryPointSlots()
-    {
-        LIMITED_METHOD_CONTRACT;
-    }
-
 #ifndef DACCESS_COMPILE
 private:
     static SIZE_T GetRequiredSlotAlignment(SlotType slotType)
@@ -77,205 +42,52 @@ private:
     }
 
 public:
-    void AddSlot_Locked(TADDR slot, SlotType slotType);
-    void Backpatch_Locked(PCODE entryPoint);
-    static void Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entryPoint);
-#endif
-
-    DISABLE_COPY(EntryPointSlots);
-};
-
-// See comment at the top of methoddescbackpatchinfo.h for a description of this and related data structures
-class MethodDescEntryPointSlots
-{
-private:
-    MethodDesc *m_methodDesc;
-
-    // This field and its data is protected by MethodDescBackpatchInfoTracker's lock
-    EntryPointSlots m_slots;
-
-public:
-    MethodDescEntryPointSlots(MethodDesc *methodDesc) : m_methodDesc(methodDesc)
-    {
-        LIMITED_METHOD_CONTRACT;
-        _ASSERTE(methodDesc != nullptr);
-    }
-
-public:
-    MethodDesc *GetMethodDesc() const
-    {
-        LIMITED_METHOD_CONTRACT;
-        return m_methodDesc;
-    }
-
-#ifndef DACCESS_COMPILE
-    EntryPointSlots *GetSlots()
-    {
-        WRAPPER_NO_CONTRACT;
-        _ASSERTE(m_methodDesc != nullptr);
-
-        return &m_slots;
-    }
-#endif
-
-    DISABLE_COPY(MethodDescEntryPointSlots);
-};
-
-class MethodDescEntryPointSlotsHashTraits
-    : public DeleteElementsOnDestructSHashTraits<NoRemoveSHashTraits<DefaultSHashTraits<MethodDescEntryPointSlots *>>>
-{
-public:
-    typedef DeleteElementsOnDestructSHashTraits<NoRemoveSHashTraits<DefaultSHashTraits<MethodDescEntryPointSlots *>>> Base;
-    typedef Base::element_t element_t;
-    typedef Base::count_t count_t;
-
-    typedef MethodDesc *key_t;
-
-    static key_t GetKey(element_t e)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return e->GetMethodDesc();
-    }
-
-    static BOOL Equals(key_t k1, key_t k2)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return k1 == k2;
-    }
-
-    static count_t Hash(key_t k)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return (count_t)((size_t)dac_cast<TADDR>(k) >> 2);
-    }
-
-    static const element_t Null() { LIMITED_METHOD_CONTRACT; return nullptr; }
-    static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == nullptr; }
-};
-
-typedef SHash<MethodDescEntryPointSlotsHashTraits> MethodDescEntryPointSlotsHash;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// MethodDescBackpatchInfo
-
-// See comment at the top of methoddescbackpatchinfo.h for a description of this and related data structures
-class MethodDescBackpatchInfo
-{
-private:
-    MethodDesc *m_methodDesc;
-
-    // Entry point slots that need to be backpatched when the method's entry point changes. This may include vtable slots, slots
-    // from virtual stub dispatch for interface methods (slots from dispatch stubs and resolve cache entries), etc. This
-    // collection only contains slots allocated in this MethodDesc's LoaderAllocator. This field and its data is protected by
-    // MethodDescBackpatchInfoTracker's lock.
-    EntryPointSlots m_slots;
-
-    // A set of LoaderAllocators from which slots that were allocated, are associated with the dependency MethodDesc and have
-    // been recorded for backpatching. For example, a derived type in a shorter-lifetime LoaderAllocator that inherits a
-    // MethodDesc from a longer-lifetime base type, would have its slot recorded in the slot's LoaderAllocator, and that
-    // LoaderAllocator would be recorded here in the MethodDesc's LoaderAllocator. This field and its data is protected by
-    // MethodDescBackpatchInfoTracker's lock.
-    LoaderAllocatorSet *m_dependentLoaderAllocators;
-
-public:
-    MethodDescBackpatchInfo(MethodDesc *methodDesc = nullptr);
-
-#ifndef DACCESS_COMPILE
-public:
-    ~MethodDescBackpatchInfo()
+    static UINT_PTR ConvertSlotAndTypePairToUINT_PTR(TADDR slot, SlotType slotType)
     {
-        LIMITED_METHOD_CONTRACT;
-
-        LoaderAllocatorSet *set = m_dependentLoaderAllocators;
-        if (set != nullptr)
-        {
-            delete set;
-        }
+        slot |= (TADDR)slotType;
+        return (UINT_PTR)slot;
     }
-#endif
 
-public:
-    MethodDesc *GetMethodDesc() const
+    static void ConvertUINT_PTRToSlotAndTypePair(UINT_PTR storedData, TADDR *pSlot, SlotType *pSlotType)
     {
-        LIMITED_METHOD_CONTRACT;
-        return m_methodDesc;
+        *pSlot = storedData;
+        *pSlotType = (SlotType)(*pSlot & SlotType_Mask);
+        *pSlot ^= *pSlotType;
     }
 
-#ifndef DACCESS_COMPILE
-public:
-    EntryPointSlots *GetSlots()
-    {
-        WRAPPER_NO_CONTRACT;
-        _ASSERTE(m_methodDesc != nullptr);
-
-        return &m_slots;
-    }
-
-public:
-    template<class Visit> void ForEachDependentLoaderAllocator_Locked(Visit visit);
-    void AddDependentLoaderAllocator_Locked(LoaderAllocator *dependentLoaderAllocator);
-    void RemoveDependentLoaderAllocator_Locked(LoaderAllocator *dependentLoaderAllocator);
+    static void Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entryPoint);
 #endif
-
-    DISABLE_COPY(MethodDescBackpatchInfo);
 };
 
-class MethodDescBackpatchInfoHashTraits
-    : public DeleteElementsOnDestructSHashTraits<NoRemoveSHashTraits<DefaultSHashTraits<MethodDescBackpatchInfo *>>>
-{
-public:
-    typedef DeleteElementsOnDestructSHashTraits<NoRemoveSHashTraits<DefaultSHashTraits<MethodDescBackpatchInfo *>>> Base;
-    typedef Base::element_t element_t;
-    typedef Base::count_t count_t;
-
-    typedef MethodDesc *key_t;
-
-    static key_t GetKey(element_t e)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return e->GetMethodDesc();
-    }
-
-    static BOOL Equals(key_t k1, key_t k2)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return k1 == k2;
-    }
-
-    static count_t Hash(key_t k)
-    {
-        LIMITED_METHOD_CONTRACT;
-        return (count_t)((size_t)dac_cast<TADDR>(k) >> 2);
-    }
-
-    static const element_t Null() { LIMITED_METHOD_CONTRACT; return nullptr; }
-    static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == nullptr; }
-};
-
-typedef SHash<MethodDescBackpatchInfoHashTraits> MethodDescBackpatchInfoHash;
-
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // MethodDescBackpatchInfoTracker
 
-// See comment at the top of methoddescbackpatchinfo.h for a description of this and related data structures
 class MethodDescBackpatchInfoTracker
 {
 private:
     static CrstStatic s_lock;
 
+    class BackpatchInfoTrackerHashTraits : public NoRemoveDefaultCrossLoaderAllocatorHashTraits<MethodDesc *, UINT_PTR>
+    {
+    };
+
+    typedef CrossLoaderAllocatorHash<BackpatchInfoTrackerHashTraits> BackpatchInfoTrackerHash;
+
     // Contains information about slots associated with the MethodDesc that were recorded for backpatching. This field and its
     // data is protected by s_lock.
-    MethodDescBackpatchInfoHash m_backpatchInfoHash;
-
-    // Contains slots associated with a MethodDesc from a dependency LoaderAllocator, which are recorded for backpatching when
-    // the MethodDesc's entry point changes. This field and its data is protected by s_lock.
-    MethodDescEntryPointSlotsHash m_dependencyMethodDescEntryPointSlotsHash;
+    BackpatchInfoTrackerHash m_backpatchInfoHash;
 
 #ifndef DACCESS_COMPILE
 public:
     static void StaticInitialize();
 #endif
 
+    void Initialize(LoaderAllocator *pLoaderAllocator)
+    {
+        WRAPPER_NO_CONTRACT;
+        m_backpatchInfoHash.Init(pLoaderAllocator);
+    }
+
 #ifdef _DEBUG
 public:
     static bool IsLockedByCurrentThread();
@@ -311,44 +123,9 @@ public:
 
 #ifndef DACCESS_COMPILE
 public:
-    MethodDescBackpatchInfo *GetBackpatchInfo_Locked(MethodDesc *methodDesc) const
-    {
-        WRAPPER_NO_CONTRACT;
-        _ASSERTE(IsLockedByCurrentThread());
-        _ASSERTE(methodDesc != nullptr);
-        _ASSERTE(MayHaveEntryPointSlotsToBackpatch(methodDesc));
-
-        return m_backpatchInfoHash.Lookup(methodDesc);
-    }
-
-    MethodDescBackpatchInfo *GetOrAddBackpatchInfo_Locked(MethodDesc *methodDesc)
-    {
-        WRAPPER_NO_CONTRACT;
-        _ASSERTE(IsLockedByCurrentThread());
-        _ASSERTE(methodDesc != nullptr);
-        _ASSERTE(MayHaveEntryPointSlotsToBackpatch(methodDesc));
-
-        MethodDescBackpatchInfo *backpatchInfo = m_backpatchInfoHash.Lookup(methodDesc);
-        if (backpatchInfo != nullptr)
-        {
-            return backpatchInfo;
-        }
-        return AddBackpatchInfo_Locked(methodDesc);
-    }
-
-private:
-    MethodDescBackpatchInfo *AddBackpatchInfo_Locked(MethodDesc *methodDesc);
-
+    void Backpatch_Locked(MethodDesc *pMethodDesc, PCODE entryPoint);
+    void AddSlotAndPatch_Locked(MethodDesc *pMethodDesc, LoaderAllocator *pLoaderAllocatorOfSlot, TADDR slot, EntryPointSlots::SlotType slotType, PCODE currentEntryPoint);
 public:
-    bool HasDependencyMethodDescEntryPointSlots() const
-    {
-        WRAPPER_NO_CONTRACT;
-        return m_dependencyMethodDescEntryPointSlotsHash.GetCount() != 0;
-    }
-
-    EntryPointSlots *GetDependencyMethodDescEntryPointSlots_Locked(MethodDesc *methodDesc);
-    EntryPointSlots *GetOrAddDependencyMethodDescEntryPointSlots_Locked(MethodDesc *methodDesc);
-    void ClearDependencyMethodDescEntryPointSlots(LoaderAllocator *loaderAllocator);
 #endif
 
     friend class ConditionalLockHolder;
@@ -356,61 +133,6 @@ public:
     DISABLE_COPY(MethodDescBackpatchInfoTracker);
 };
 
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Inline and template definitions
-
-#ifndef DACCESS_COMPILE
-
-inline void EntryPointSlots::AddSlot_Locked(TADDR slot, SlotType slotType)
-{
-    WRAPPER_NO_CONTRACT;
-    static_assert_no_msg(SlotType_Count <= sizeof(INT32));
-    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
-    _ASSERTE(slot != NULL);
-    _ASSERTE(!(slot & SlotType_Mask));
-    _ASSERTE(slotType >= SlotType_Normal);
-    _ASSERTE(slotType < SlotType_Count);
-    _ASSERTE(IS_ALIGNED((SIZE_T)slot, GetRequiredSlotAlignment(slotType)));
-
-    m_slots.Append(slot | slotType);
-}
-
-#endif // DACCESS_COMPILE
-
-inline MethodDescBackpatchInfo::MethodDescBackpatchInfo(MethodDesc *methodDesc)
-    : m_methodDesc(methodDesc), m_dependentLoaderAllocators(nullptr)
-{
-    LIMITED_METHOD_CONTRACT;
-    _ASSERTE(
-        methodDesc == nullptr ||
-        MethodDescBackpatchInfoTracker::MayHaveEntryPointSlotsToBackpatch(PTR_MethodDesc(methodDesc)));
-}
-
-#ifndef DACCESS_COMPILE
-
-template<class Visit>
-inline void MethodDescBackpatchInfo::ForEachDependentLoaderAllocator_Locked(Visit visit)
-{
-    WRAPPER_NO_CONTRACT;
-    _ASSERTE(MethodDescBackpatchInfoTracker::IsLockedByCurrentThread());
-    _ASSERTE(m_methodDesc != nullptr);
-
-    LoaderAllocatorSet *set = m_dependentLoaderAllocators;
-    if (set == nullptr)
-    {
-        return;
-    }
-
-    for (LoaderAllocatorSet::Iterator it = set->Begin(), itEnd = set->End(); it != itEnd; ++it)
-    {
-        visit(*it);
-    }
-}
-
-#endif // DACCESS_COMPILE
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
 #undef DISABLE_COPY
 
 #endif // !CROSSGEN_COMPILE
index 3298de3..52e4508 100644 (file)
@@ -1393,6 +1393,25 @@ DEFINE_CLASS(OBJECT_EQUALITYCOMPARER, CollectionsGeneric, ObjectEqualityComparer
 
 DEFINE_CLASS(INATTRIBUTE, Interop, InAttribute)
 
+DEFINE_CLASS_U(CompilerServices,           GCHeapHash,                      GCHeapHashObject)
+DEFINE_FIELD_U(_data,                      GCHeapHashObject,                _data)
+DEFINE_FIELD_U(_count,                     GCHeapHashObject,                _count)
+DEFINE_FIELD_U(_deletedCount,              GCHeapHashObject,                _deletedCount)
+
+DEFINE_CLASS(GCHEAPHASH, CompilerServices, GCHeapHash)
+
+DEFINE_CLASS_U(CompilerServices,           LAHashDependentHashTracker,      LAHashDependentHashTrackerObject)
+DEFINE_FIELD_U(_dependentHandle,           LAHashDependentHashTrackerObject,_dependentHandle)
+DEFINE_FIELD_U(_loaderAllocator,           LAHashDependentHashTrackerObject,_loaderAllocator)
+
+DEFINE_CLASS(LAHASHDEPENDENTHASHTRACKER, CompilerServices, LAHashDependentHashTracker)
+
+DEFINE_CLASS_U(CompilerServices,           LAHashKeyToTrackers,             LAHashKeyToTrackersObject)
+DEFINE_FIELD_U(_trackerOrTrackerSet,       LAHashKeyToTrackersObject,       _trackerOrTrackerSet)
+DEFINE_FIELD_U(_laLocalKeyValueStore,      LAHashKeyToTrackersObject,       _laLocalKeyValueStore)
+
+DEFINE_CLASS(LAHASHKEYTOTRACKERS, CompilerServices, LAHashKeyToTrackers)
+
 #undef DEFINE_CLASS
 #undef DEFINE_METHOD
 #undef DEFINE_FIELD
index fb1294d..2949f79 100644 (file)
@@ -2220,3 +2220,20 @@ void ExceptionObject::GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF *
 #endif // !defined(DACCESS_COMPILE)
 
 }
+
+bool LAHashDependentHashTrackerObject::IsLoaderAllocatorLive()
+{
+    return (ObjectFromHandle(_dependentHandle) != NULL);
+}
+
+void LAHashDependentHashTrackerObject::GetDependentAndLoaderAllocator(OBJECTREF *pLoaderAllocatorRef, GCHEAPHASHOBJECTREF *pGCHeapHash)
+{
+    OBJECTREF primary = ObjectFromHandle(_dependentHandle);
+    if (pLoaderAllocatorRef != NULL)
+        *pLoaderAllocatorRef = primary;
+
+    IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
+    // Secondary is tracked only if primary is non-null
+    if (pGCHeapHash != NULL)
+        *pGCHeapHash = (GCHEAPHASHOBJECTREF)(OBJECTREF)((primary != NULL) ? mgr->GetDependentHandleSecondary(_dependentHandle) : NULL);
+}
index 4f5a0b8..6bc3a74 100644 (file)
@@ -851,6 +851,7 @@ typedef Array<U2>   U2Array;
 typedef Array<WCHAR>   CHARArray;
 typedef Array<U4>   U4Array;
 typedef Array<U8>   U8Array;
+typedef Array<UPTR> UPTRArray;
 typedef PtrArray    PTRArray;  
 
 typedef DPTR(I1Array)   PTR_I1Array;
@@ -865,6 +866,7 @@ typedef DPTR(U2Array)   PTR_U2Array;
 typedef DPTR(CHARArray) PTR_CHARArray;
 typedef DPTR(U4Array)   PTR_U4Array;
 typedef DPTR(U8Array)   PTR_U8Array;
+typedef DPTR(UPTRArray) PTR_UPTRArray;
 typedef DPTR(PTRArray)  PTR_PTRArray;
 
 class StringObject;
@@ -882,6 +884,7 @@ typedef REF<BOOLArray>  BOOLARRAYREF;
 typedef REF<U2Array>    U2ARRAYREF;
 typedef REF<U4Array>    U4ARRAYREF;
 typedef REF<U8Array>    U8ARRAYREF;
+typedef REF<UPTRArray>  UPTRARRAYREF;
 typedef REF<CHARArray>  CHARARRAYREF;
 typedef REF<PTRArray>   PTRARRAYREF;  // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
 typedef REF<StringObject> STRINGREF;
@@ -900,6 +903,7 @@ typedef PTR_BOOLArray   BOOLARRAYREF;
 typedef PTR_U2Array     U2ARRAYREF;
 typedef PTR_U4Array     U4ARRAYREF;
 typedef PTR_U8Array     U8ARRAYREF;
+typedef PTR_UPTRArray   UPTRARRAYREF;
 typedef PTR_CHARArray   CHARARRAYREF;
 typedef PTR_PTRArray    PTRARRAYREF;  // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
 typedef PTR_StringObject STRINGREF;
@@ -2796,4 +2800,133 @@ typedef REF<ExceptionObject> EXCEPTIONREF;
 typedef PTR_ExceptionObject EXCEPTIONREF;
 #endif // USE_CHECKED_OBJECTREFS
 
+class GCHeapHashObject : public Object
+{
+#ifdef DACCESS_COMPILE
+    friend class ClrDataAccess;
+#endif
+    friend class GCHeap;
+    friend class JIT_TrialAlloc;
+    friend class CheckAsmOffsets;
+    friend class COMString;
+    friend class MscorlibBinder;
+
+    private:
+    BASEARRAYREF _data;
+    INT32 _count;
+    INT32 _deletedCount;
+
+    public:
+    INT32 GetCount() { LIMITED_METHOD_CONTRACT; return _count; }
+    void IncrementCount(bool replacingDeletedItem)
+    {
+        LIMITED_METHOD_CONTRACT; 
+        ++_count;
+        if (replacingDeletedItem)
+            --_deletedCount;
+    }
+
+    void DecrementCount(bool deletingItem)
+    {
+        LIMITED_METHOD_CONTRACT;
+        --_count;
+        if (deletingItem)
+            ++_deletedCount;
+    }
+    INT32 GetDeletedCount() { LIMITED_METHOD_CONTRACT; return _deletedCount; }
+    void SetDeletedCountToZero() { LIMITED_METHOD_CONTRACT; _deletedCount = 0; }
+    INT32 GetCapacity() { LIMITED_METHOD_CONTRACT; if (_data == NULL) return 0; else return (_data->GetNumComponents()); }
+    BASEARRAYREF GetData() { LIMITED_METHOD_CONTRACT; return _data; }
+
+    void SetTable(BASEARRAYREF data)
+    {
+        STATIC_CONTRACT_NOTHROW;
+        STATIC_CONTRACT_GC_NOTRIGGER;
+        STATIC_CONTRACT_MODE_COOPERATIVE;
+
+        SetObjectReference((OBJECTREF*)&_data, (OBJECTREF)data, GetAppDomain());
+    }
+
+    protected:
+    GCHeapHashObject() {LIMITED_METHOD_CONTRACT; }
+   ~GCHeapHashObject() {LIMITED_METHOD_CONTRACT; }
+};
+
+typedef DPTR(GCHeapHashObject)  PTR_GCHeapHashObject;
+
+#ifdef USE_CHECKED_OBJECTREFS
+typedef REF<GCHeapHashObject> GCHEAPHASHOBJECTREF;
+#else   // USE_CHECKED_OBJECTREFS
+typedef PTR_GCHeapHashObject GCHEAPHASHOBJECTREF;
+#endif // USE_CHECKED_OBJECTREFS
+
+class LAHashDependentHashTrackerObject : public Object
+{
+#ifdef DACCESS_COMPILE
+    friend class ClrDataAccess;
+#endif
+    friend class CheckAsmOffsets;
+    friend class MscorlibBinder;
+
+    private:
+    OBJECTHANDLE _dependentHandle;
+    LoaderAllocator* _loaderAllocator;
+
+    public:
+    bool IsLoaderAllocatorLive();
+    bool IsTrackerFor(LoaderAllocator *pLoaderAllocator)
+    {
+        if (pLoaderAllocator != _loaderAllocator)
+            return false;
+        
+        return IsLoaderAllocatorLive();
+    }
+
+    void GetDependentAndLoaderAllocator(OBJECTREF *pLoaderAllocatorRef, GCHEAPHASHOBJECTREF *pGCHeapHash);
+
+    // Be careful with this. This isn't safe to use unless something is keeping the LoaderAllocator live, or there is no intention to dereference this pointer
+    LoaderAllocator* GetLoaderAllocatorUnsafe()
+    {
+        return _loaderAllocator;
+    }
+
+    void Init(OBJECTHANDLE dependentHandle, LoaderAllocator* loaderAllocator)
+    {
+        LIMITED_METHOD_CONTRACT;
+        _dependentHandle = dependentHandle;
+        _loaderAllocator = loaderAllocator;
+    }
+};
+
+class LAHashKeyToTrackersObject : public Object
+{
+#ifdef DACCESS_COMPILE
+    friend class ClrDataAccess;
+#endif
+    friend class CheckAsmOffsets;
+    friend class MscorlibBinder;
+
+    public:
+    // _trackerOrTrackerSet is either a reference to a LAHashDependentHashTracker, or to a GCHeapHash of LAHashDependentHashTracker objects.
+    OBJECTREF _trackerOrTrackerSet;
+    // _laLocalKeyValueStore holds an object that represents a Key value (which must always be valid for the lifetime of the 
+    // CrossLoaderAllocatorHeapHash, and the values which must also be valid for that entire lifetime. When a value might
+    // have a shorter lifetime it is accessed through the _trackerOrTrackerSet variable, which allows access to hashtables which
+    // are associated with that remote loaderallocator through a dependent handle, so that lifetime can be managed.
+    OBJECTREF _laLocalKeyValueStore;
+};
+
+typedef DPTR(LAHashDependentHashTrackerObject)  PTR_LAHashDependentHashTrackerObject;
+typedef DPTR(LAHashKeyToTrackersObject) PTR_LAHashKeyToTrackersObject;
+
+
+#ifdef USE_CHECKED_OBJECTREFS
+typedef REF<LAHashDependentHashTrackerObject> LAHASHDEPENDENTHASHTRACKERREF;
+typedef REF<LAHashKeyToTrackersObject> LAHASHKEYTOTRACKERSREF;
+#else   // USE_CHECKED_OBJECTREFS
+typedef PTR_LAHashDependentHashTrackerObject LAHASHDEPENDENTHASHTRACKERREF;
+typedef PTR_LAHashKeyToTrackersObject LAHASHKEYTOTRACKERSREF;
+#endif // USE_CHECKED_OBJECTREFS
+
+
 #endif // _OBJECT_H_
index d04a5d0..a08a2d1 100644 (file)
@@ -1964,6 +1964,8 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
             return pCode;
         }
 
+        _ASSERTE(!MayHaveEntryPointSlotsToBackpatch()); // This path doesn't lock the MethodDescBackpatchTracker as it should only
+                                                        // happen for jump-stampable or non-versionable methods
         SetCodeEntryPoint(pCode);
     }
     else
index b7527bf..d074284 100644 (file)
@@ -613,39 +613,44 @@ HRESULT ReJitManager::UpdateActiveILVersions(
     BOOL fEESuspended = FALSE;
     SHash<CodeActivationBatchTraits>::Iterator beginIter = mgrToCodeActivationBatch.Begin();
     SHash<CodeActivationBatchTraits>::Iterator endIter = mgrToCodeActivationBatch.End();
-    for (SHash<CodeActivationBatchTraits>::Iterator iter = beginIter; iter != endIter; iter++)
-    {
-        CodeActivationBatch * pCodeActivationBatch = *iter;
-        CodeVersionManager * pCodeVersionManager = pCodeActivationBatch->m_pCodeVersionManager;
 
-        int cMethodsToActivate = pCodeActivationBatch->m_methodsToActivate.Count();
-        if (cMethodsToActivate == 0)
-        {
-            continue;
-        }
+    {
+        MethodDescBackpatchInfoTracker::ConditionalLockHolder lockHolder;
 
+        for (SHash<CodeActivationBatchTraits>::Iterator iter = beginIter; iter != endIter; iter++)
         {
-            // SetActiveILCodeVersions takes the SystemDomain crst, which needs to be acquired before the 
-            // ThreadStore crsts
-            SystemDomain::LockHolder lh;
+            CodeActivationBatch * pCodeActivationBatch = *iter;
+            CodeVersionManager * pCodeVersionManager = pCodeActivationBatch->m_pCodeVersionManager;
 
-            if(!fEESuspended)
+            int cMethodsToActivate = pCodeActivationBatch->m_methodsToActivate.Count();
+            if (cMethodsToActivate == 0)
             {
-                // As a potential future optimization we could speculatively try to update the jump stamps without
-                // suspending the runtime. That needs to be plumbed through BatchUpdateJumpStamps though.
-                ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
-                fEESuspended = TRUE;
+                continue;
             }
 
-            _ASSERTE(ThreadStore::HoldingThreadStore());
-            hr = pCodeVersionManager->SetActiveILCodeVersions(pCodeActivationBatch->m_methodsToActivate.Ptr(), pCodeActivationBatch->m_methodsToActivate.Count(), fEESuspended, &errorRecords);
-            if (FAILED(hr))
-                break;
+            {
+                // SetActiveILCodeVersions takes the SystemDomain crst, which needs to be acquired before the 
+                // ThreadStore crsts
+                SystemDomain::LockHolder lh;
+
+                if(!fEESuspended)
+                {
+                    // As a potential future optimization we could speculatively try to update the jump stamps without
+                    // suspending the runtime. That needs to be plumbed through BatchUpdateJumpStamps though.
+                    ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
+                    fEESuspended = TRUE;
+                }
+
+                _ASSERTE(ThreadStore::HoldingThreadStore());
+                hr = pCodeVersionManager->SetActiveILCodeVersions(pCodeActivationBatch->m_methodsToActivate.Ptr(), pCodeActivationBatch->m_methodsToActivate.Count(), fEESuspended, &errorRecords);
+                if (FAILED(hr))
+                    break;
+            }
+        }
+        if (fEESuspended)
+        {
+            ThreadSuspend::RestartEE(FALSE, TRUE);
         }
-    }
-    if (fEESuspended)
-    {
-        ThreadSuspend::RestartEE(FALSE, TRUE);
     }
 
     if (FAILED(hr))
index dd40d8c..f091d03 100644 (file)
@@ -505,6 +505,7 @@ void TieredCompilationManager::ResumeCountingCalls(MethodDesc* pMethodDesc)
 {
     WRAPPER_NO_CONTRACT;
     _ASSERTE(pMethodDesc != nullptr);
+    MethodDescBackpatchInfoTracker::ConditionalLockHolder lockHolder(pMethodDesc->MayHaveEntryPointSlotsToBackpatch());
 
     EX_TRY
     {
@@ -718,6 +719,8 @@ void TieredCompilationManager::ActivateCodeVersion(NativeCodeVersion nativeCodeV
     // code version will activate then.
     ILCodeVersion ilParent;
     HRESULT hr = S_OK;
+    MethodDescBackpatchInfoTracker::ConditionalLockHolder lockHolder;
+
     {
         // As long as we are exclusively using precode publishing for tiered compilation
         // methods this first attempt should succeed