summaryrefslogtreecommitdiff
path: root/src/vm
diff options
context:
space:
mode:
authorDavid Wrighton <davidwr@microsoft.com>2019-02-14 17:07:14 -0800
committerGitHub <noreply@github.com>2019-02-14 17:07:14 -0800
commit423d2a3b91feea18ab361da04d5cc24bdff157d0 (patch)
treee59d54a5c48322a09ef1a4c5325378c9f60171dd /src/vm
parent89e78f42ba11beaa81635a75cf593a3713dba176 (diff)
downloadcoreclr-423d2a3b91feea18ab361da04d5cc24bdff157d0.tar.gz
coreclr-423d2a3b91feea18ab361da04d5cc24bdff157d0.tar.bz2
coreclr-423d2a3b91feea18ab361da04d5cc24bdff157d0.zip
Replace multi-loaderallocator hash implementation in MethodDescBackpatchInfo (#22285)
* 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
Diffstat (limited to 'src/vm')
-rw-r--r--src/vm/appdomain.cpp11
-rw-r--r--src/vm/codeversion.cpp2
-rw-r--r--src/vm/crossloaderallocatorhash.h197
-rw-r--r--src/vm/crossloaderallocatorhash.inl1216
-rw-r--r--src/vm/frames.h2
-rw-r--r--src/vm/gcheaphashtable.h142
-rw-r--r--src/vm/gcheaphashtable.inl530
-rw-r--r--src/vm/loaderallocator.cpp12
-rw-r--r--src/vm/loaderallocator.hpp3
-rw-r--r--src/vm/method.cpp47
-rw-r--r--src/vm/methoddescbackpatchinfo.cpp153
-rw-r--r--src/vm/methoddescbackpatchinfo.h326
-rw-r--r--src/vm/mscorlib.h19
-rw-r--r--src/vm/object.cpp17
-rw-r--r--src/vm/object.h133
-rw-r--r--src/vm/prestub.cpp2
-rw-r--r--src/vm/rejit.cpp55
-rw-r--r--src/vm/tieredcompilation.cpp3
18 files changed, 2361 insertions, 509 deletions
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index f7bedff69f..42e26c99d1 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -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
diff --git a/src/vm/codeversion.cpp b/src/vm/codeversion.cpp
index e8ace2890d..5286815845 100644
--- a/src/vm/codeversion.cpp
+++ b/src/vm/codeversion.cpp
@@ -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
index 0000000000..09068752b0
--- /dev/null
+++ b/src/vm/crossloaderallocatorhash.h
@@ -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
index 0000000000..51bce4e1ae
--- /dev/null
+++ b/src/vm/crossloaderallocatorhash.inl
@@ -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
diff --git a/src/vm/frames.h b/src/vm/frames.h
index 8847641198..3d092bd06e 100644
--- a/src/vm/frames.h
+++ b/src/vm/frames.h
@@ -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
index 0000000000..a795ce6fa9
--- /dev/null
+++ b/src/vm/gcheaphashtable.h
@@ -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
index 0000000000..f2256bea1e
--- /dev/null
+++ b/src/vm/gcheaphashtable.inl
@@ -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
diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp
index a54b0932b5..ae57e1f692 100644
--- a/src/vm/loaderallocator.cpp
+++ b/src/vm/loaderallocator.cpp
@@ -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
//
diff --git a/src/vm/loaderallocator.hpp b/src/vm/loaderallocator.hpp
index dc63ab9ad9..100c0d7e27 100644
--- a/src/vm/loaderallocator.hpp
+++ b/src/vm/loaderallocator.hpp
@@ -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
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index 69849f3a9f..70d79ca515 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -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
diff --git a/src/vm/methoddescbackpatchinfo.cpp b/src/vm/methoddescbackpatchinfo.cpp
index 386786c6ba..571007cdfc 100644
--- a/src/vm/methoddescbackpatchinfo.cpp
+++ b/src/vm/methoddescbackpatchinfo.cpp
@@ -17,24 +17,6 @@
#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
-
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/vm/methoddescbackpatchinfo.h b/src/vm/methoddescbackpatchinfo.h
index 3aa2b13255..c5d92a29da 100644
--- a/src/vm/methoddescbackpatchinfo.h
+++ b/src/vm/methoddescbackpatchinfo.h
@@ -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
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index 3298de3eee..52e4508446 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -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
diff --git a/src/vm/object.cpp b/src/vm/object.cpp
index fb1294d5f5..2949f79066 100644
--- a/src/vm/object.cpp
+++ b/src/vm/object.cpp
@@ -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);
+}
diff --git a/src/vm/object.h b/src/vm/object.h
index 4f5a0b81df..6bc3a74471 100644
--- a/src/vm/object.h
+++ b/src/vm/object.h
@@ -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_
diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
index d04a5d0a54..a08a2d1a10 100644
--- a/src/vm/prestub.cpp
+++ b/src/vm/prestub.cpp
@@ -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
diff --git a/src/vm/rejit.cpp b/src/vm/rejit.cpp
index b7527bfe74..d0742840d4 100644
--- a/src/vm/rejit.cpp
+++ b/src/vm/rejit.cpp
@@ -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))
diff --git a/src/vm/tieredcompilation.cpp b/src/vm/tieredcompilation.cpp
index dd40d8c3da..f091d0348d 100644
--- a/src/vm/tieredcompilation.cpp
+++ b/src/vm/tieredcompilation.cpp
@@ -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