Skip to content

Commit 5da8f31

Browse files
VSadovjkotas
andauthored
Completely lock-free ClassLoader::LookupTypeKey (#61346)
* embedded size * next array * read consistency while rehashing * comments * remove CRST_UNSAFE_ANYMODE and COOP mode in the callers * typo * fix 32bit builds * couple changes from review. * Walk the buckets in resize. * remove a `REVIEW:` comment. * Update src/coreclr/vm/dacenumerablehash.inl PR review suggestion Co-authored-by: Jan Kotas <jkotas@microsoft.com> * remove use of `auto` * DAC stuff * Constructor and GrowTable are not called by DAC, no need for DPTR, added a comment about a cast. * SKIP_SPECIAL_SLOTS * More compact dac_cast in GetNext Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent b68328a commit 5da8f31

File tree

8 files changed

+182
-163
lines changed

8 files changed

+182
-163
lines changed

src/coreclr/vm/clsload.cpp

Lines changed: 8 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,27 +1061,8 @@ void ClassLoader::TryEnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level)
10611061
#endif // DACCESS_COMPILE
10621062
}
10631063

1064-
// This is separated out to avoid the overhead of C++ exception handling in the non-locking case.
10651064
/* static */
1066-
TypeHandle ClassLoader::LookupTypeKeyUnderLock(TypeKey *pKey,
1067-
EETypeHashTable *pTable,
1068-
CrstBase *pLock)
1069-
{
1070-
WRAPPER_NO_CONTRACT;
1071-
SUPPORTS_DAC;
1072-
1073-
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
1074-
GCX_MAYBE_COOP_NO_THREAD_BROKEN(!IsGCThread());
1075-
1076-
CrstHolder ch(pLock);
1077-
return pTable->GetValue(pKey);
1078-
}
1079-
1080-
/* static */
1081-
TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey,
1082-
EETypeHashTable *pTable,
1083-
CrstBase *pLock,
1084-
BOOL fCheckUnderLock)
1065+
TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable)
10851066
{
10861067
CONTRACTL {
10871068
NOTHROW;
@@ -1090,26 +1071,15 @@ TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey,
10901071
PRECONDITION(CheckPointer(pKey));
10911072
PRECONDITION(pKey->IsConstructed());
10921073
PRECONDITION(CheckPointer(pTable));
1093-
PRECONDITION(!fCheckUnderLock || CheckPointer(pLock));
10941074
MODE_ANY;
10951075
SUPPORTS_DAC;
10961076
} CONTRACTL_END;
10971077

1098-
TypeHandle th;
1099-
1100-
if (fCheckUnderLock)
1101-
{
1102-
th = LookupTypeKeyUnderLock(pKey, pTable, pLock);
1103-
}
1104-
else
1105-
{
1106-
th = pTable->GetValue(pKey);
1107-
}
1108-
return th;
1078+
return pTable->GetValue(pKey);
11091079
}
11101080

11111081
/* static */
1112-
TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock)
1082+
TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey)
11131083
{
11141084
CONTRACTL {
11151085
NOTHROW;
@@ -1124,39 +1094,12 @@ TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock
11241094
Module *pLoaderModule = ComputeLoaderModule(pKey);
11251095
PREFIX_ASSUME(pLoaderModule!=NULL);
11261096

1127-
return LookupTypeKey(pKey,
1128-
pLoaderModule->GetAvailableParamTypes(),
1129-
&pLoaderModule->GetClassLoader()->m_AvailableTypesLock,
1130-
fCheckUnderLock);
1097+
return LookupTypeKey(pKey, pLoaderModule->GetAvailableParamTypes());
11311098
}
11321099

11331100

11341101
/* static */
11351102
TypeHandle ClassLoader::LookupTypeHandleForTypeKey(TypeKey *pKey)
1136-
{
1137-
WRAPPER_NO_CONTRACT;
1138-
SUPPORTS_DAC;
1139-
1140-
// Make an initial lookup without taking any locks.
1141-
TypeHandle th = LookupTypeHandleForTypeKeyInner(pKey, FALSE);
1142-
1143-
// A non-null TypeHandle for the above lookup indicates success
1144-
// A null TypeHandle only indicates "well, it might have been there,
1145-
// try again with a lock". This kind of negative result will
1146-
// only happen while accessing the underlying EETypeHashTable
1147-
// during a resize, i.e. very rarely. In such a case, we just
1148-
// perform the lookup again, but indicate that appropriate locks
1149-
// should be taken.
1150-
1151-
if (th.IsNull())
1152-
{
1153-
th = LookupTypeHandleForTypeKeyInner(pKey, TRUE);
1154-
}
1155-
1156-
return th;
1157-
}
1158-
/* static */
1159-
TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fCheckUnderLock)
11601103
{
11611104
CONTRACTL
11621105
{
@@ -1184,7 +1127,7 @@ TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fChe
11841127
// If the thing is not NGEN'd then this may
11851128
// be different to pPreferredZapModule. If they are the same then
11861129
// we can reuse the results of the lookup above.
1187-
TypeHandle thLM = LookupInLoaderModule(pKey, fCheckUnderLock);
1130+
TypeHandle thLM = LookupInLoaderModule(pKey);
11881131
if (!thLM.IsNull())
11891132
{
11901133
return thLM;
@@ -2055,11 +1998,10 @@ VOID ClassLoader::Init(AllocMemTracker *pamTracker)
20551998
CrstAvailableClass,
20561999
CRST_REENTRANCY);
20572000

2058-
// This lock is taken within the classloader whenever we have to insert a new param. type into the table
2059-
// This lock also needs to be taken for a read operation in a GC_NOTRIGGER scope, thus the ANYMODE flag.
2001+
// This lock is taken within the classloader whenever we have to insert a new param. type into the table.
20602002
m_AvailableTypesLock.Init(
2061-
CrstAvailableParamTypes,
2062-
(CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD));
2003+
CrstAvailableParamTypes,
2004+
CRST_DEBUGGER_THREAD);
20632005

20642006
#ifdef _DEBUG
20652007
CorTypeInfo::CheckConsistency();
@@ -3104,9 +3046,6 @@ TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
31043046
Module *pLoaderModule = ComputeLoaderModule(pTypeKey);
31053047
EETypeHashTable *pTable = pLoaderModule->GetAvailableParamTypes();
31063048

3107-
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
3108-
GCX_COOP();
3109-
31103049
CrstHolder ch(&pLoaderModule->GetClassLoader()->m_AvailableTypesLock);
31113050

31123051
// The type could have been loaded by a different thread as side-effect of avoiding deadlocks caused by LoadsTypeViolation
@@ -3121,9 +3060,6 @@ TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
31213060
Module *pModule = pTypeKey->GetModule();
31223061
mdTypeDef typeDef = pTypeKey->GetTypeToken();
31233062

3124-
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
3125-
GCX_COOP();
3126-
31273063
CrstHolder ch(&pModule->GetClassLoader()->m_AvailableTypesLock);
31283064

31293065
// ! We cannot fail after this point.

src/coreclr/vm/clsload.hpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -899,21 +899,13 @@ class ClassLoader
899899
ClassLoadLevel level = CLASS_LOADED,
900900
const InstantiationContext *pInstContext = NULL);
901901

902-
static TypeHandle LookupTypeKeyUnderLock(TypeKey *pKey,
903-
EETypeHashTable *pTable,
904-
CrstBase *pLock);
902+
static TypeHandle LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable);
905903

906-
static TypeHandle LookupTypeKey(TypeKey *pKey,
907-
EETypeHashTable *pTable,
908-
CrstBase *pLock,
909-
BOOL fCheckUnderLock);
910-
911-
static TypeHandle LookupInLoaderModule(TypeKey* pKey, BOOL fCheckUnderLock);
904+
static TypeHandle LookupInLoaderModule(TypeKey* pKey);
912905

913906
// Lookup a handle in the appropriate table
914907
// (declaring module for TypeDef or loader-module for constructed types)
915908
static TypeHandle LookupTypeHandleForTypeKey(TypeKey *pTypeKey);
916-
static TypeHandle LookupTypeHandleForTypeKeyInner(TypeKey *pTypeKey, BOOL fCheckUnderLock);
917909

918910
static void DECLSPEC_NORETURN ThrowTypeLoadException(TypeKey *pKey, UINT resIDWhy);
919911

src/coreclr/vm/dacenumerablehash.h

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ class DacEnumerableHashTable
9696
void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
9797
#endif // DACCESS_COMPILE
9898

99+
private:
100+
struct VolatileEntry;
101+
typedef DPTR(struct VolatileEntry) PTR_VolatileEntry;
102+
struct VolatileEntry
103+
{
104+
VALUE m_sValue; // The derived-class format of an entry
105+
PTR_VolatileEntry m_pNextEntry; // Pointer to the next entry in the bucket chain (or NULL)
106+
DacEnumerableHashValue m_iHashValue; // The hash value associated with the entry
107+
};
108+
99109
protected:
100110
// This opaque structure provides enumeration context when walking the set of entries which share a common
101111
// hash code. Initialized by BaseFindFirstEntryByHash and read/updated by BaseFindNextEntryByHash.
@@ -106,6 +116,7 @@ class DacEnumerableHashTable
106116
TADDR m_pEntry; // The entry the caller is currently looking at (or NULL to begin
107117
// with). This is a VolatileEntry* and should always be a target address
108118
// not a DAC PTR_.
119+
DPTR(PTR_VolatileEntry) m_curBuckets; // The bucket table we are working with.
109120
};
110121

111122
// This opaque structure provides enumeration context when walking all entries in the table. Initialized
@@ -176,16 +187,9 @@ class DacEnumerableHashTable
176187
PTR_Module m_pModule;
177188

178189
private:
190+
private:
179191
// Internal implementation details. Nothing of interest to sub-classers for here on.
180-
181-
struct VolatileEntry;
182-
typedef DPTR(struct VolatileEntry) PTR_VolatileEntry;
183-
struct VolatileEntry
184-
{
185-
VALUE m_sValue; // The derived-class format of an entry
186-
PTR_VolatileEntry m_pNextEntry; // Pointer to the next entry in the bucket chain (or NULL)
187-
DacEnumerableHashValue m_iHashValue; // The hash value associated with the entry
188-
};
192+
DPTR(VALUE) BaseFindFirstEntryByHashCore(DPTR(PTR_VolatileEntry) curBuckets, DacEnumerableHashValue iHash, LookupContext* pContext);
189193

190194
#ifndef DACCESS_COMPILE
191195
// Determine loader heap to be used for allocation of entries and bucket lists.
@@ -206,11 +210,27 @@ class DacEnumerableHashTable
206210
return m_pBuckets;
207211
}
208212

213+
// our bucket table uses two extra slots - slot [0] contains the length of the table,
214+
// slot [1] will contain the next version of the table if it resizes
215+
static const int SLOT_LENGTH = 0;
216+
static const int SLOT_NEXT = 1;
217+
// normal slots start at slot #2
218+
static const int SKIP_SPECIAL_SLOTS = 2;
219+
220+
static DWORD GetLength(DPTR(PTR_VolatileEntry) buckets)
221+
{
222+
return (DWORD)dac_cast<TADDR>(buckets[SLOT_LENGTH]);
223+
}
224+
225+
static DPTR(PTR_VolatileEntry) GetNext(DPTR(PTR_VolatileEntry) buckets)
226+
{
227+
return dac_cast<DPTR(PTR_VolatileEntry)>(buckets[SLOT_NEXT]);
228+
}
229+
209230
// Loader heap provided at construction time. May be NULL (in which case m_pModule must *not* be NULL).
210231
LoaderHeap *m_pHeap;
211232

212233
DPTR(PTR_VolatileEntry) m_pBuckets; // Pointer to a simple bucket list (array of VolatileEntry pointers)
213-
DWORD m_cBuckets; // Count of buckets in the above array (always non-zero)
214234
DWORD m_cEntries; // Count of elements
215235
};
216236

0 commit comments

Comments
 (0)