Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 52 additions & 20 deletions src/coreclr/vm/loaderallocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#endif
#include "comcallablewrapper.h"

//#define ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP 1

#define STUBMANAGER_RANGELIST(stubManager) (stubManager::g_pManager->GetRangeList())

UINT64 LoaderAllocator::cLoaderAllocatorsCreated = 1;
Expand Down Expand Up @@ -125,6 +127,9 @@ void LoaderAllocator::AddReference()

_ASSERTE((m_cReferences > (UINT32)0) && (m_cReferences != (UINT32)-1));
InterlockedIncrement((LONG *)&m_cReferences);
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::AddReference LA %d(%p) %d\n", m_nLoaderAllocator, this, (int)m_cReferences.Load());
#endif
}
#endif //!DACCESS_COMPILE

Expand Down Expand Up @@ -162,6 +167,9 @@ BOOL LoaderAllocator::AddReferenceIfAlive()

if (cOriginalReferences == cReferencesLocalSnapshot)
{ // The exchange happened
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::AddReferenceIfAlive LA %d(%p) %d\n", this->m_nLoaderAllocator, this, (int)m_cReferences.Load());
#endif
return TRUE;
}
// Let's spin till we are the only thread to modify this value
Expand Down Expand Up @@ -191,6 +199,9 @@ BOOL LoaderAllocator::Release()

_ASSERTE((m_cReferences > (UINT32)0) && (m_cReferences != (UINT32)-1));
LONG cNewReferences = InterlockedDecrement((LONG *)&m_cReferences);
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::Release LA %d(%p) %d\n", this->m_nLoaderAllocator, this, (int)m_cReferences.Load());
#endif
return (cNewReferences == 0);
#else //DACCESS_COMPILE

Expand Down Expand Up @@ -226,6 +237,10 @@ BOOL LoaderAllocator::CheckAddReference_Unlocked(LoaderAllocator *pOtherLA)
m_LoaderAllocatorReferences.Add(pOtherLA);

// Notify the other LoaderAllocator that a reference exists
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::CheckAddReference_Unlocked LA %d(%p) to LA %d(%p)\n", m_nLoaderAllocator, this, pOtherLA->m_nLoaderAllocator, pOtherLA, (int)pOtherLA->m_cReferences.Load());
#endif

pOtherLA->AddReference();
return TRUE;
}
Expand Down Expand Up @@ -358,45 +373,42 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain
// List of LoaderAllocators being deleted
LoaderAllocator * pFirstDestroyedLoaderAllocator = NULL;

#if 0
// Debug logic for debugging the loader allocator gc.
AppDomain::AssemblyIterator i;
{
// Iterate through every loader allocator, marking as we go
CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock());
CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock());

CollectibleAssemblyHolder<Assembly *> pAssembly;

#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
// Debug logic for debugging the loader allocator gc.
/* Iterate through every loader allocator, and print its current state */
AppDomain::AssemblyIterator iData;
iData = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(
i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeExecution | kIncludeLoaded | kIncludeCollected));
CollectibleAssemblyHolder<Assembly *> pAssembly;

while (iData.Next_Unlocked(pAssembly.This()))
while (i.Next_Unlocked(pAssembly.This()))
{
if (pAssembly != NULL)
{
LoaderAllocator * pLoaderAllocator = pAssembly->GetLoaderAllocator();
if (pLoaderAllocator->IsCollectible())
{
minipal_log_print_info("LA %p ReferencesTo %d\n", pLoaderAllocator, pLoaderAllocator->m_cReferences);
minipal_log_print_info("LA %d(%p) ReferencesTo %d\n", pLoaderAllocator->m_nLoaderAllocator, pLoaderAllocator, (int)pLoaderAllocator->m_cReferences.Load());
LoaderAllocatorSet::Iterator iter = pLoaderAllocator->m_LoaderAllocatorReferences.Begin();
while (iter != pLoaderAllocator->m_LoaderAllocatorReferences.End())
{
LoaderAllocator * pAllocator = *iter;
minipal_log_print_info("LARefTo: %p\n", pAllocator);
minipal_log_print_info("LARefTo: %d(%p)\n", pAllocator->m_nLoaderAllocator, pAllocator);
iter++;
}
}
}
}
}
#endif //0

AppDomain::AssemblyIterator i;
{
// Iterate through every loader allocator, marking as we go
CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock());
CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock());
#endif // ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP

i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeExecution | kIncludeLoaded | kIncludeCollected));
CollectibleAssemblyHolder<Assembly *> pAssembly;

while (i.Next_Unlocked(pAssembly.This()))
{
Expand All @@ -406,7 +418,12 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain
if (pLoaderAllocator->IsCollectible())
{
if (pLoaderAllocator->IsAlive())
{
// This mark is a deep mark, it will mark all LoaderAllocators that this one references too (recursively),
// even ones that are not alive. Note that the caller of the current function has decremented reference count
// of the loader allocator we are going to release and also all of its dependencies.
pLoaderAllocator->Mark();
}
}
}
}
Expand Down Expand Up @@ -508,13 +525,16 @@ void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocat
}
CONTRACTL_END;

#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("-------LoaderAllocator::GCLoaderAllocators LA %d(%p) %d\n", pOriginalLoaderAllocator->m_nLoaderAllocator, pOriginalLoaderAllocator, pOriginalLoaderAllocator->m_cReferences.Load());
#endif
// List of LoaderAllocators being deleted
LoaderAllocator * pFirstDestroyedLoaderAllocator = NULL;

AppDomain* pAppDomain = AppDomain::GetCurrentDomain();

// Collect all LoaderAllocators that don't have anymore DomainAssemblies alive
// Note: that it may not collect our pOriginalLoaderAllocator in case this
// Note: that it will not collect our pOriginalLoaderAllocator in case this
// LoaderAllocator hasn't loaded any DomainAssembly. We handle this case in the next loop.
// Note: The removed LoaderAllocators are not reachable outside of this function anymore, because we
// removed them from the assembly list
Expand Down Expand Up @@ -550,10 +570,13 @@ void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocat
pDomainLoaderAllocatorDestroyIterator = pDomainLoaderAllocatorDestroyIterator->m_pLoaderAllocatorDestroyNext;
}

// If the original LoaderAllocator was not processed, it is most likely a LoaderAllocator without any loaded DomainAssembly
// If the original LoaderAllocator was not processed, it is a LoaderAllocator without any loaded DomainAssembly
// But we still want to collect it so we add it to the list of LoaderAllocator to destroy
if (!isOriginalLoaderAllocatorFound && !pOriginalLoaderAllocator->IsAlive())
if (!isOriginalLoaderAllocatorFound && !pOriginalLoaderAllocator->Id()->HasAttachedDynamicAssemblies() && !pOriginalLoaderAllocator->IsAlive())
{
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::GCLoaderAllocators FORCED unload due to no DomainAssembly LA %d(%p) %d\n", pOriginalLoaderAllocator->m_nLoaderAllocator, pOriginalLoaderAllocator, (int)pOriginalLoaderAllocator->m_cReferences.Load());
#endif
pOriginalLoaderAllocator->m_pLoaderAllocatorDestroyNext = pFirstDestroyedLoaderAllocator;
pFirstDestroyedLoaderAllocator = pOriginalLoaderAllocator;
}
Expand All @@ -562,6 +585,9 @@ void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocat
pDomainLoaderAllocatorDestroyIterator = pFirstDestroyedLoaderAllocator;
while (pDomainLoaderAllocatorDestroyIterator != NULL)
{
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::GCLoaderAllocators DESTROY LA %d(%p) %d\n", pDomainLoaderAllocatorDestroyIterator->m_nLoaderAllocator, pDomainLoaderAllocatorDestroyIterator, (int)pDomainLoaderAllocatorDestroyIterator->m_cReferences.Load());
#endif
_ASSERTE(!pDomainLoaderAllocatorDestroyIterator->IsAlive());

DomainAssemblyIterator domainAssemblyIt(pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete);
Expand Down Expand Up @@ -642,6 +668,9 @@ BOOL LoaderAllocator::Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator)
{
STRESS_LOG1(LF_CLASSLOADER, LL_INFO100, "Begin LoaderAllocator::Destroy for loader allocator %p\n", reinterpret_cast<void *>(static_cast<PTR_LoaderAllocator>(pLoaderAllocator)));
LoaderAllocatorID *pID = pLoaderAllocator->Id();
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::Destroy LA %d(%p) %d\n", pLoaderAllocator->m_nLoaderAllocator, pLoaderAllocator, pLoaderAllocator->m_cReferences.Load());
#endif

{
GCX_COOP();
Expand Down Expand Up @@ -1044,6 +1073,9 @@ void LoaderAllocator::ActivateManagedTracking()
// There is now one external reference to this LoaderAllocator (the managed scout)
_ASSERTE(m_cReferences == (UINT32)-1);
m_cReferences = (UINT32)1;
#ifdef ENABLE_LOG_LOADER_ALLOCATOR_CLEANUP
minipal_log_print_info("LoaderAllocator::ActivateManagedTracking LA %d(%p) %d\n", m_nLoaderAllocator, this, (int)m_cReferences.Load());
#endif

LOADERALLOCATORREF loaderAllocator = (LOADERALLOCATORREF)ObjectFromHandle(m_hLoaderAllocatorObjectHandle);
loaderAllocator->SetNativeLoaderAllocator(this);
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/loaderallocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,14 @@ class LoaderAllocatorID
m_pValue = value;
};
VOID Init();
bool HasAttachedDynamicAssemblies()
{
if (m_type == LAT_Assembly && m_pDomainAssembly != NULL)
{
return true;
}
return false;
}
LoaderAllocatorType GetType();
VOID AddDomainAssembly(DomainAssembly* pDomainAssembly);
DomainAssemblyIterator GetDomainAssemblyIterator();
Expand Down
9 changes: 9 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/AssemblyA.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CLRTestKind>BuildOnly</CLRTestKind>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="ClassA.cs" />
</ItemGroup>
</Project>
13 changes: 13 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/AssemblyB.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CLRTestKind>BuildOnly</CLRTestKind>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="ClassB.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="AssemblyA.csproj" />
<ProjectReference Include="AssemblyC.csproj" />
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/AssemblyC.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CLRTestKind>BuildOnly</CLRTestKind>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="ClassC.cs" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/ClassA.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace AssemblyA;

public class ClassA
{
}
19 changes: 19 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/ClassB.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using AssemblyC;

namespace AssemblyB;

public class ClassB
{
public string GetMessage()
{
var b = new GenericClass<AssemblyA.ClassA>();
var c = new ClassC();
return $"Hello from Assembly B! -> {c.GetMessage()} -> {b.ToString()}";
}
}

public class GenericClass<T>
{
}
11 changes: 11 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_116953/ClassC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace AssemblyC;

public class ClassC
{
public string GetMessage()
{
return "Hello from Assembly C!";
}
}
Loading
Loading