-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completely lock-free ClassLoader::LookupTypeKey #61346
Changes from 8 commits
1b003e3
5330325
c5db8c4
c264f69
e8e7d6e
b9b4813
749695a
5569c66
7c3cd08
f8b1021
067cefb
1516f6b
efe6043
f455581
e874b3c
c7eb7e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1061,27 +1061,8 @@ void ClassLoader::TryEnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level) | |
#endif // DACCESS_COMPILE | ||
} | ||
|
||
// This is separated out to avoid the overhead of C++ exception handling in the non-locking case. | ||
/* static */ | ||
TypeHandle ClassLoader::LookupTypeKeyUnderLock(TypeKey *pKey, | ||
EETypeHashTable *pTable, | ||
CrstBase *pLock) | ||
{ | ||
WRAPPER_NO_CONTRACT; | ||
SUPPORTS_DAC; | ||
|
||
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC | ||
GCX_MAYBE_COOP_NO_THREAD_BROKEN(!IsGCThread()); | ||
|
||
CrstHolder ch(pLock); | ||
return pTable->GetValue(pKey); | ||
} | ||
|
||
/* static */ | ||
TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, | ||
EETypeHashTable *pTable, | ||
CrstBase *pLock, | ||
BOOL fCheckUnderLock) | ||
TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, EETypeHashTable *pTable) | ||
{ | ||
CONTRACTL { | ||
NOTHROW; | ||
|
@@ -1090,26 +1071,15 @@ TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey, | |
PRECONDITION(CheckPointer(pKey)); | ||
PRECONDITION(pKey->IsConstructed()); | ||
PRECONDITION(CheckPointer(pTable)); | ||
PRECONDITION(!fCheckUnderLock || CheckPointer(pLock)); | ||
MODE_ANY; | ||
SUPPORTS_DAC; | ||
} CONTRACTL_END; | ||
|
||
TypeHandle th; | ||
|
||
if (fCheckUnderLock) | ||
{ | ||
th = LookupTypeKeyUnderLock(pKey, pTable, pLock); | ||
} | ||
else | ||
{ | ||
th = pTable->GetValue(pKey); | ||
} | ||
return th; | ||
return pTable->GetValue(pKey); | ||
} | ||
|
||
/* static */ | ||
TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock) | ||
TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey) | ||
{ | ||
CONTRACTL { | ||
NOTHROW; | ||
|
@@ -1124,39 +1094,12 @@ TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock | |
Module *pLoaderModule = ComputeLoaderModule(pKey); | ||
PREFIX_ASSUME(pLoaderModule!=NULL); | ||
|
||
return LookupTypeKey(pKey, | ||
pLoaderModule->GetAvailableParamTypes(), | ||
&pLoaderModule->GetClassLoader()->m_AvailableTypesLock, | ||
fCheckUnderLock); | ||
return LookupTypeKey(pKey, pLoaderModule->GetAvailableParamTypes()); | ||
} | ||
|
||
|
||
/* static */ | ||
TypeHandle ClassLoader::LookupTypeHandleForTypeKey(TypeKey *pKey) | ||
{ | ||
WRAPPER_NO_CONTRACT; | ||
SUPPORTS_DAC; | ||
|
||
// Make an initial lookup without taking any locks. | ||
TypeHandle th = LookupTypeHandleForTypeKeyInner(pKey, FALSE); | ||
|
||
// A non-null TypeHandle for the above lookup indicates success | ||
// A null TypeHandle only indicates "well, it might have been there, | ||
// try again with a lock". This kind of negative result will | ||
// only happen while accessing the underlying EETypeHashTable | ||
// during a resize, i.e. very rarely. In such a case, we just | ||
// perform the lookup again, but indicate that appropriate locks | ||
// should be taken. | ||
|
||
if (th.IsNull()) | ||
{ | ||
th = LookupTypeHandleForTypeKeyInner(pKey, TRUE); | ||
} | ||
|
||
return th; | ||
} | ||
/* static */ | ||
TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fCheckUnderLock) | ||
{ | ||
CONTRACTL | ||
{ | ||
|
@@ -1184,7 +1127,7 @@ TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fChe | |
// If the thing is not NGEN'd then this may | ||
// be different to pPreferredZapModule. If they are the same then | ||
// we can reuse the results of the lookup above. | ||
TypeHandle thLM = LookupInLoaderModule(pKey, fCheckUnderLock); | ||
TypeHandle thLM = LookupInLoaderModule(pKey); | ||
if (!thLM.IsNull()) | ||
{ | ||
return thLM; | ||
|
@@ -2055,11 +1998,10 @@ VOID ClassLoader::Init(AllocMemTracker *pamTracker) | |
CrstAvailableClass, | ||
CRST_REENTRANCY); | ||
|
||
// This lock is taken within the classloader whenever we have to insert a new param. type into the table | ||
// This lock also needs to be taken for a read operation in a GC_NOTRIGGER scope, thus the ANYMODE flag. | ||
// This lock is taken within the classloader whenever we have to insert a new param. type into the table. | ||
m_AvailableTypesLock.Init( | ||
CrstAvailableParamTypes, | ||
(CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); | ||
CrstAvailableParamTypes, | ||
CRST_DEBUGGER_THREAD); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hoyosjs - If debugger only does lookups, then it will no longer take this look, then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know we can't load assemblies, but it's possible that a FuncEval can load a type I believe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is funceval running on debugger thread? I assumed that
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried removing I just noticed that But do not think |
||
|
||
#ifdef _DEBUG | ||
CorTypeInfo::CheckConsistency(); | ||
|
@@ -3104,9 +3046,6 @@ TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd) | |
Module *pLoaderModule = ComputeLoaderModule(pTypeKey); | ||
EETypeHashTable *pTable = pLoaderModule->GetAvailableParamTypes(); | ||
|
||
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC | ||
GCX_COOP(); | ||
|
||
CrstHolder ch(&pLoaderModule->GetClassLoader()->m_AvailableTypesLock); | ||
|
||
// 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) | |
Module *pModule = pTypeKey->GetModule(); | ||
mdTypeDef typeDef = pTypeKey->GetTypeToken(); | ||
|
||
// m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC | ||
GCX_COOP(); | ||
|
||
CrstHolder ch(&pModule->GetClassLoader()->m_AvailableTypesLock); | ||
|
||
// ! We cannot fail after this point. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
LookupMap<TYPE>
can be read concurrently while writer has not left the write lock yet (so on ARM the reader may occasionally not see all the writes). I will look at this separately. Maybe it is ok for some subtle reason.It would be a separate issue anyways