Skip to content

Conversation

alexey-zakharov
Copy link
Contributor

Purpose of this PR

Improve logic of computing LoaderAllocator for a generic type instance to avoid AssemblyLoadContext leaks.
Resolves #111611

Problem:
ClassLoader::ComputeLoaderModuleWorker uses a LoaderAllocator creation number to determine the latest LoaderAllocator to place a class in. However the method does not account the number of the type definition itself when calculating the latest number and this can lead into a situation when the generic instance is put into the older LoaderAllocator.

E.g. if we have type ClassA from AssemblyLoadContext A with number 2 and type ClassB from AssemblyLoadContext B with number 5 the resulting loader allocator for the type ClassB would correspond to AssemblyLoadContext A. If ClassB contains a static, such static would be then placed under the LoaderAllocator A, preventing AssemblyLoadContext B from unloading.

Proposed solution:
The PR updates the ComputeLoaderModuleWorker logic to account for the LoaderAllocator of a defining type, so we use it if it has the latest creation number.

(cherry picked from commit b3a73f3)

@alexey-zakharov
Copy link
Contributor Author

cc @davidwrighton

I would be also happy to add a test for this case - which suite should I use for it? StaticsUnloaded.cs?

@jkotas jkotas requested a review from davidwrighton January 22, 2025 15:20
pLoaderModule = pDefinitionModule;
}
// Use the definition module as the loader module by default
Module *pLoaderModule = pDefinitionModule;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if pDefinitionModule != null we effectively fall through to it if it is collectible or not at the end, so I've decided to remove the if block

…r allocator of a generic type instance

(cherry picked from commit b3a73f3)
@alexey-zakharov alexey-zakharov force-pushed the dotnet-upstream/fix-computeloaderallocator branch from 5d15fef to 8aa00c8 Compare January 22, 2025 17:09
Copy link
Member

@davidwrighton davidwrighton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you note, a test would be helpful. However, since the behavior of this sort of computation is not actually specified and may change in the future without warning, I'd rather not merge it in with existing tests, and would prefer this to be a separate test (and for the test to have some comments in it indicating that failure is reasonable when running on different runtimes). I'm pretty flexible on naming though, as I don't have a strong opinion. I'm notoriously terrible at naming stuff.

@alexey-zakharov
Copy link
Contributor Author

would prefer this to be a separate test (and for the test to have some comments in it indicating that failure is reasonable when running on different runtimes)

I've looked at the best location for the test and found that there is src/libraries/System.Runtime.Loader/tests/CollectibleAssemblyLoadContextTest.cs suite which tests ALC unloadability and seems like a perfect fit for the issue - would it be acceptable to have a test there?

  • added Unload_TwoCollectibleWithOneAssemblyAndOneInstanceReferencingAnotherThroughGenericStatic test which tests the failing scenario
  • updated issue links since the mentioned issue is closed now

Assert.Contains("System.Runtime.Loader.DefaultAssemblyLoadContext", alc.ToString());
Assert.Contains(alc, AssemblyLoadContext.All);
Assert.Contains(Assembly.GetCallingAssembly(), alc.Assemblies);
Assert.Contains(typeof(int).Assembly, alc.Assemblies);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the test because it was failing as the calling assembly for the test method is xunit assembly which is not in the Default ALC and I suppose is test runner implementation specific
CorLib assembly which is where type of int is is more stable check parameter imho

@alexey-zakharov
Copy link
Contributor Author

@davidwrighton I don't see any suspicious test failures - do you think the changes are good to go?

@davidwrighton davidwrighton merged commit bd24074 into dotnet:main Mar 4, 2025
122 of 125 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 3, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-AssemblyLoader-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LoaderAllocator selection logic for a generic type instance does not account for a LoaderAllocator of type itself

2 participants