Move the cache for the marshaler methods to be stored on the RuntimeType instead of in a ConditionalWeakTable#126621
Conversation
…ype instead of in a ConditionalWeakTable Should fix dotnet#126608
|
@MihuBot benchmark Interop.StructureToPtr |
|
Tagging subscribers to this area: @dotnet/area-system-reflection |
There was a problem hiding this comment.
Pull request overview
This PR moves the cache for Marshal.LayoutTypeMarshalerMethods from a ConditionalWeakTable<Type, …> to the per-RuntimeType generic cache infrastructure to address the reported interop performance regressions (issue #126608).
Changes:
- Adds
LayoutTypeMarshalerMethodsstorage toRuntimeType.CompositeCacheEntryso it can live in the existingRuntimeTypegeneric cache. - Refactors
LayoutTypeMarshalerMethodsto implementRuntimeType.IGenericCacheEntry<T>and retrieves it viaRuntimeType.GetOrCreateCacheEntry<T>(). - Switches from delegate creation (
CreateDelegate) to caching raw function pointers (MethodHandle.GetFunctionPointer) for the marshaler methods.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.GenericCache.cs |
Adds a new composite-cache field to hold the marshaler-methods cache on RuntimeType. |
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs |
Reworks LayoutTypeMarshalerMethods to use RuntimeType cache entries and cached function pointers instead of a ConditionalWeakTable. |
| private static readonly ConditionalWeakTable<Type, LayoutTypeMarshalerMethods> s_marshalerCache = []; | ||
| internal static LayoutTypeMarshalerMethods GetMarshalMethodsForType(Type t) | ||
| { | ||
| return ((RuntimeType)t).GetOrCreateCacheEntry<LayoutTypeMarshalerMethods>(); |
There was a problem hiding this comment.
GetMarshalMethodsForType takes a Type but now unconditionally casts to RuntimeType. This can throw InvalidCastException if any internal caller passes a non-RuntimeType (e.g., TypeBuilder), which is a behavior change from the prior ConditionalWeakTable<Type, ...> implementation.
Consider tightening the contract by changing the parameter type to RuntimeType, or adding a fast check/throw with an ArgumentException message that clearly states the requirement (or keeping a fallback path for non-RuntimeType).
| return ((RuntimeType)t).GetOrCreateCacheEntry<LayoutTypeMarshalerMethods>(); | |
| if (t is not RuntimeType runtimeType) | |
| { | |
| throw new ArgumentException("Type must be a RuntimeType.", nameof(t)); | |
| } | |
| return runtimeType.GetOrCreateCacheEntry<LayoutTypeMarshalerMethods>(); |
Interop.StructureToPtr
|
Should fix #126608