Skip to content

Commit b1a4178

Browse files
Add MetadataUpdateHandlerAttribute (#50954)
* Add MetadataUpdateHandlerAttribute And at least the beginning of reflection cache clearing support. * Address PR feedback * Update src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com> Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
1 parent 3d30dc4 commit b1a4178

11 files changed

Lines changed: 154 additions & 0 deletions

File tree

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeModule.cs" />
200200
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeParameterInfo.cs" />
201201
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
202+
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\RuntimeTypeMetadataUpdateHandler.cs" />
202203
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
203204
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CrossLoaderAllocatorHashHelpers.cs" />
204205
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Reflection.Metadata;
5+
6+
[assembly: MetadataUpdateHandler(typeof(RuntimeTypeMetadataUpdateHandler))]
7+
8+
namespace System.Reflection.Metadata
9+
{
10+
/// <summary>Metadata update handler used to clear a Type's reflection cache in response to a metadata update notification.</summary>
11+
internal static class RuntimeTypeMetadataUpdateHandler
12+
{
13+
public static void BeforeUpdate(Type? type)
14+
{
15+
if (type is RuntimeType rt)
16+
{
17+
rt.ClearCache();
18+
}
19+
20+
// TODO: https://github.com/dotnet/runtime/issues/50938
21+
// Do we need to clear the cache on other types, e.g. ones derived from this one?
22+
// Do we need to clear a cache on any other kinds of types?
23+
}
24+
}
25+
}

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,6 +2418,37 @@ private RuntimeTypeCache InitializeCache()
24182418
return cache;
24192419
}
24202420

2421+
internal void ClearCache()
2422+
{
2423+
// If there isn't a GCHandle yet, there's nothing more to do.
2424+
if (Volatile.Read(ref m_cache) == IntPtr.Zero)
2425+
{
2426+
return;
2427+
}
2428+
2429+
// Loop until the cache is successfully zero'd out.
2430+
do
2431+
{
2432+
// If the GCHandle doesn't wrap a cache yet, there's nothing more to do.
2433+
RuntimeTypeCache? existingCache = (RuntimeTypeCache?)GCHandle.InternalGet(m_cache);
2434+
if (existingCache is null)
2435+
{
2436+
return;
2437+
}
2438+
2439+
// Create a new, empty cache to replace the old one and try to substitute it in.
2440+
var newCache = new RuntimeTypeCache(this);
2441+
if (ReferenceEquals(GCHandle.InternalCompareExchange(m_cache, newCache, existingCache), existingCache))
2442+
{
2443+
// We were successful, so there's nothing more to do.
2444+
return;
2445+
}
2446+
2447+
// We raced with someone else to initialize the cache. Try again.
2448+
}
2449+
while (true);
2450+
}
2451+
24212452
private string? GetDefaultMemberName()
24222453
{
24232454
return Cache.GetDefaultMemberName();

src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
<type fullname="System.Diagnostics.DebuggerVisualizerAttribute">
3030
<attribute internal="RemoveAttributeInstances" />
3131
</type>
32+
33+
<!-- Hot reload attributes-->
34+
<type fullname="System.Reflection.Metadata.MetadataUpdateHandlerAttribute">
35+
<attribute internal="RemoveAttributeInstances" />
36+
</type>
3237
</assembly>
3338

3439
<assembly fullname="System.Private.CoreLib" feature="System.Diagnostics.Tracing.EventSource.IsSupported" featurevalue="false">

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@
616616
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TypeDelegator.cs" />
617617
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TypeFilter.cs" />
618618
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TypeInfo.cs" />
619+
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Metadata\MetadataUpdateHandlerAttribute.cs" />
619620
<Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventArgs.cs" />
620621
<Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventHandler.cs" />
621622
<Compile Include="$(MSBuildThisFileDirectory)System\Resources\FastResourceComparer.cs" />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
namespace System.Reflection.Metadata
7+
{
8+
/// <summary>Specifies a type that should receive notifications of metadata updates.</summary>
9+
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
10+
public sealed class MetadataUpdateHandlerAttribute : Attribute
11+
{
12+
/// <summary>Initializes the attribute.</summary>
13+
/// <param name="handlerType">A type that handles metadata updates and that should be notified when any occur.</param>
14+
public MetadataUpdateHandlerAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type handlerType) =>
15+
HandlerType = handlerType;
16+
17+
/// <summary>Gets the type that handles metadata updates and that should be notified when any occur.</summary>
18+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
19+
public Type HandlerType { get; }
20+
}
21+
}

src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ public static partial class AssemblyExtensions
1212
public unsafe static bool TryGetRawMetadata(this System.Reflection.Assembly assembly, out byte* blob, out int length) { throw null; }
1313
public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDelta, ReadOnlySpan<byte> ilDelta, ReadOnlySpan<byte> pdbDelta) { throw null; }
1414
}
15+
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)]
16+
public sealed class MetadataUpdateHandlerAttribute : System.Attribute
17+
{
18+
public MetadataUpdateHandlerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type handlerType) { }
19+
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)]
20+
public System.Type HandlerType { get { throw null; } }
21+
}
1522
}
1623
namespace System.Runtime.Loader
1724
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Xunit;
5+
6+
namespace System.Reflection.Metadata
7+
{
8+
public class MetadataUpdateHandlerAttributeTest
9+
{
10+
[Fact]
11+
public void Ctor_RoundtripType()
12+
{
13+
Type t = typeof(MetadataUpdateHandlerAttributeTest);
14+
var a = new MetadataUpdateHandlerAttribute(t);
15+
Assert.Same(t, a.HandlerType);
16+
}
17+
}
18+
}

src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<Compile Include="CollectibleAssemblyLoadContextTest.cs" />
1414
<Compile Include="ContextualReflection.cs" />
1515
<Compile Include="CustomTPALoadContext.cs" />
16+
<Compile Include="MetadataUpdateHandlerAttributeTest.cs" />
1617
<Compile Include="ResourceAssemblyLoadContext.cs" />
1718
<Compile Include="SatelliteAssemblies.cs" />
1819
<Compile Include="LoaderLinkTest.cs" />

src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
<Compile Include="System\Reflection\ObfuscateAssemblyAttributeTests.cs" />
207207
<Compile Include="System\Reflection\ObfuscationAttributeTests.cs" />
208208
<Compile Include="System\Reflection\PointerTests.cs" />
209+
<Compile Include="System\Reflection\ReflectionCacheTests.cs" />
209210
<Compile Include="System\Reflection\ReflectionContextTests.cs" />
210211
<Compile Include="System\Reflection\ReflectionTypeLoadExceptionTests.cs" />
211212
<Compile Include="System\Reflection\StrongNameKeyPairTests.cs" />

0 commit comments

Comments
 (0)