-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Use AssemblyLoadContext-aware caches in TypeDescriptor to support unloading of assemblies cached by TypeDescriptor #114619
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
Use AssemblyLoadContext-aware caches in TypeDescriptor to support unloading of assemblies cached by TypeDescriptor #114619
Conversation
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.
Copilot reviewed 6 out of 9 changed files in this pull request and generated no comments.
Files not reviewed (3)
- src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/UnloadableTestTypes/UnloadableTestTypes.csproj: Language not supported
Comments suppressed due to low confidence (1)
src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs:104
- Consider increasing the number of garbage collection attempts or using a more robust timeout mechanism to ensure that the assembly unloads reliably across different environments.
for (int i = 0; weakRef.IsAlive && i < 10; i++)
MetadataUpdateHandler is not meant to be used to prepare caches for AssemblyLoadContext unloading. |
The simplest way to avoid the overhead is to skip the caching completely for collectible types. We do that in number of places in this repo. |
thanks for the suggestion! |
If the performance regression is not acceptable, it is an option to have two caches - one for collectible types and one for non-collectible types. It avoids the performance overhead of weak handles for non-colllectible types. |
@jkotas to clarify - would you be ok with a solution that keeps existing cache for types in non-collectible ALCs and WeakHashtable for the types in collectible ALCs (for both TypeDescriptor and ReflectTypeDescriptionProvider cache levels), correct? I like this approach, as it gives correctness and performance at a relatively low cost of calling Type.IsCollectible 😄 |
src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs
Outdated
Show resolved
Hide resolved
I'm not sure on what issue this PR is current solving. Currently it depends on hot reload APIs to be called to clear caches, but that already works for hot reload purposes AFAIK. It doesn't solve the more general issue of unloading a collectible ALC. |
It would be fine with me. We have similar split in other places. |
@steveharter yes, I'm going to rework it to split the caches next week. Although there is one case which may need a cache clear approach - |
007504d
to
78c71fe
Compare
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.
Pull Request Overview
This PR fixes an issue with ReflectTypeDescriptionProvider by ensuring that the cached type is completely removed during cache cleanup, allowing for proper unloading of AssemblyLoadContexts.
- Updated cache data structures to use ContextAwareConcurrentDictionary and ContextAwareHashtable.
- Added new tests to verify that unloadable types are correctly cleared from the cache.
- Refactored related helper classes (WeakHashtable, ContextAwareConcurrentDictionary, and ContextAwareHashtable) for improved cache management.
Reviewed Changes
Copilot reviewed 7 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
UnloadableTestTypes.cs | Added sample unloadable type and associated attribute for testing. |
TypeDescriptorTests.cs | Introduced new tests to validate cache clearance and proper unloading. |
WeakHashtable.cs | Modified weak reference handling for enumeration in caching. |
TypeDescriptor.cs | Updated cache implementation to use ContextAware data structures. |
ReflectTypeDescriptionProvider.cs | Refactored to use new ContextAwareConcurrentDictionary and improved threading comments. |
ContextAwareHashtable.cs | Added new implementation to handle collectible types in caching. |
ContextAwareConcurrentDictionary.cs | Introduced new concurrent dictionary to support collectible MemberInfo keys. |
Files not reviewed (4)
- src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/UnloadableTestTypes/UnloadableTestTypes.csproj: Language not supported
src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs
Outdated
Show resolved
Hide resolved
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.
Pull Request Overview
This PR fixes a caching issue in ReflectTypeDescriptionProvider by ensuring that collectible types are fully removed from static caches, thereby allowing AssemblyLoadContext unload. Key changes include:
- Transition from ConcurrentDictionary/Hashtable to context-aware collections (ContextAwareConcurrentDictionary and ContextAwareHashtable) for collectible types.
- Updates in TypeDescriptor and ReflectTypeDescriptionProvider to use the new collections.
- Addition of tests to confirm that unloadable types are not retained in the caches.
Reviewed Changes
Copilot reviewed 7 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
tests/UnloadableTestTypes/UnloadableTestTypes.cs | Added sample unloadable types for testing. |
tests/TypeDescriptorTests.cs | Introduced a new AssemblyLoadContext test to verify cache cleanup. |
src/System/ComponentModel/WeakHashtable.cs | Updated weak referencing via indexer override. |
src/System/ComponentModel/TypeDescriptor.cs | Switched to ContextAware collections and refined lock comments. |
src/System/ComponentModel/ReflectTypeDescriptionProvider.cs | Modified caching to use context-aware collections and improved comments. |
src/System/ComponentModel/ContextAwareHashtable.cs | New helper to conditionally use weak references for collectible keys. |
src/System/ComponentModel/ContextAwareConcurrentDictionary.cs | New concurrent dictionary variant for handling collectible keys. |
Files not reviewed (4)
- src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/UnloadableTestTypes/UnloadableTestTypes.csproj: Language not supported
...m.ComponentModel.TypeConverter/src/System/ComponentModel/ContextAwareConcurrentDictionary.cs
Outdated
Show resolved
Hide resolved
@steveharter @jkotas I've updated the implementation to use That give us a good balance between functional correctness and performance as:
|
...tem.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs
Outdated
Show resolved
Hide resolved
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.
Pull Request Overview
This PR fixes type caching issues to ensure unloadable assembly types are properly removed from static caches and introduces context-aware collections to hold weak references for collectible types.
- Updated tests to validate that unloadable types are cleared.
- Replaced standard collections with ContextAwareConcurrentDictionary and ContextAwareHashtable in caching logic.
- Added helper classes for weak referencing collectible types.
Reviewed Changes
Copilot reviewed 7 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
tests/UnloadableTestTypes/UnloadableTestTypes.cs | Added simple unloadable test types. |
tests/TypeDescriptorTests.cs | Added assembly load context and unload test to exercise cache clearing. |
src/System/ComponentModel/WeakHashtable.cs | Updated enumerator behavior to capture keys during iteration. |
src/System/ComponentModel/TypeDescriptor.cs | Migrated to context-aware collections for caching. |
src/System/ComponentModel/ReflectTypeDescriptionProvider.cs | Transitioned to context-aware caches and ensured proper removal criteria. |
src/System/ComponentModel/ContextAwareHashtable.cs | Introduced new hashtable for wrapping collectible keys in weak references. |
src/System/ComponentModel/ContextAwareConcurrentDictionary.cs | Added a new concurrent dictionary for mapping collectible keys using ConditionalWeakTable. |
Files not reviewed (4)
- src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj: Language not supported
- src/libraries/System.ComponentModel.TypeConverter/tests/UnloadableTestTypes/UnloadableTestTypes.csproj: Language not supported
...m.ComponentModel.TypeConverter/src/System/ComponentModel/ContextAwareConcurrentDictionary.cs
Outdated
Show resolved
Hide resolved
c5bed6c
to
96bc926
Compare
a9d6943
to
ff60836
Compare
@ericstj I've addressed your comments, thanks for the review once more! Main change - Make WeakHashTtable a wrapper around ConditionalWeakTable: |
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.
Thank you for addressing the feedback. This is looking good, just one minor renaming suggestion for consistency.
...aries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ContextAwareHashtable.cs
Outdated
Show resolved
Hide resolved
…loadability for the TypeDescriptor class
…ditionalWeakTable to ensure values can be also colelcted
ff60836
to
43e50c6
Compare
awesome, thanks for the help with the PR! |
@ericstj do you have any more suggestions? should the PR be merged? 😄 |
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.
Sorry for taking a while in getting back to you, I missed the notification on this.
@alexey-zakharov do you need this in net10.0 or is main sufficient? |
thanks for merging @ericstj !
backporting to net10.0 would be ideal as we were planning to upgrade to it |
I would definitely like to see some bake time for this change before it makes it to 10.0 - the challenge is that the bar for 10.0 will only be getting higher from here on. @vitek-karas do you have opinions on this? How can we gain confidence in this fix in the scenarios that matter for #30656? |
I agree that this change is not the riskier side - mostly because it's used under the hood by many components and potential issues might take a while to show up. |
Fixes #30656 - TypeDescriptor caches keeping strong references to the types from collectible assemblies.
Motivation
TypeDescriptor
uses static caches to amortize attributes retrieval and store user providers. The caches useType
-to-object approach and capture strong reference to the Type and dependent objects. That also includesReflectTypeDescriptionProvider
) implementation. Strong reference to the Type prevents collectible types and thus collectible assemblies from being collected and unloaded.Changes
The PR applies the "context aware table" pattern used in coreclr codebase to split caching of collectible and non-collectible types into 2 tables - standard Hashtable/Dictionary for non-collectible types and WeakHashtable/ConditionalWeaktable for collectible types.
ContextAwareConcurrentHashtable
andContextAwareHashtable
helper classes which leverageDependentHandle
/WeakReference
for collectible types.TypeDescription
andReflectTypeDescriptionProvider
implementations to ensure there are no strong references from static caches to types from unloadable assemblies.Note that the collectible Type used as a key will be only collected once
AssemblyLoadContext.Unload
is called since the type instance for collectible types is allocated byLoaderAllocator
and stored in itsm_slots
table for the whole lifetime ofLoaderAllocator
. Thus all custom collectible providers will be kept alive until the point when their parent AssemblyLoadContext is explicitly unloaded.The performance impact of the change may be observed for the collectible types - dereferencing
DependentHandle
andWeakReference
adds overhead and may introduce additional entries in the handle tables. And the cost of callingMemberInfo.IsCollectible
is added to all queries.The PR addresses
#114620#30656 issue.Testing
TypeDescriptorTests.TypeDescriptor_DoesNotKeepUnloadableTypes
test to validate caching for collectible types.