Avoid clearing thread local handles for already unloaded loader #253
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Motivation:
Fix crash in
ThreadLocalBlock::FreeTLMcode on thread exit which happens during Assembly unloading when Assembly contains a class with aThreadStaticvariable.Details:
There seem to be a race condition between LoaderAllocator cleanup during garbage collection and thread locals cleanup on thread exit. Racing stacks are the following:
ALC/LoaderAllocator cleanup
Thread exit with threadlocal cleanup
and exception thrown with the following details
loaderAllocatorvalue from theLOADERALLOCATORREF loaderAllocator = (LOADERALLOCATORREF)ObjectFromHandle(m_hLoaderAllocatorObjectHandle);call is NULL which makes sense, because finalizer thread just disposed data for some of the same LoaderAllocator assemblies.Note: enabling
COR_PRF_MONITOR_MODULE_LOADSCLR profiler flag increases chances for the race condition as it slows down a bit complete loader destruction.Changes:
I've looked at the following options as a solution to the problem:
loaderAllocatorI think skipping unloaded loaded is the best option wrt performance/correctness considerations as loader deletion will cleanup all internal data, we won't access memory which may be already returned to pool/system and we won't lock other threads.