Skip to content

Commit

Permalink
Fix allocation of empty array in the frozen heap for collectible types (
Browse files Browse the repository at this point in the history
#100444) (#100509)

* Fix allocation of empty array in the frozen heap for collectible types (#100437)

* Remove Optimize from csproj

* Add test for generic with static

* Apply suggestions from code review

* Better test

* Disable tests on Mono

---------

Co-authored-by: Alexandre Mutel <[email protected]>
  • Loading branch information
jkotas and xoofx authored Apr 4, 2024
1 parent 7d45915 commit 7ae2d00
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9393,7 +9393,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
CORINFO_FIELD_INFO fi;
eeGetFieldInfo(&fldToken, CORINFO_ACCESS_SET, &fi);
unsigned flagsToCheck = CORINFO_FLG_FIELD_STATIC | CORINFO_FLG_FIELD_FINAL;
if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)
if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&
((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))
{
#ifdef FEATURE_READYTORUN
if (opts.IsReadyToRun())
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/frozenobjectheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);
_ASSERT(!type->Collectible());

// Currently we don't support frozen objects with special alignment requirements
// TODO: We should also give up on arrays of doubles on 32-bit platforms.
Expand Down
112 changes: 112 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using Xunit;

public class Runtime_100437
{
[Fact]
[SkipOnMono("PlatformDetection.IsPreciseGcSupported false on mono", TestPlatforms.Any)]
public static void TestNonCollectibleType() => TestCollectibleReadOnlyStatics(nameof(NonCollectibleType));

[Fact]
[SkipOnMono("PlatformDetection.IsPreciseGcSupported false on mono", TestPlatforms.Any)]
public static void TestNonCollectibleTypeInSharedGenericCode() => TestCollectibleReadOnlyStatics(nameof(NonCollectibleTypeInSharedGenericCode));

[Fact]
[SkipOnMono("PlatformDetection.IsPreciseGcSupported false on mono", TestPlatforms.Any)]
public static void TestNonCollectibleArrayTypeInSharedGenericCode() => TestCollectibleReadOnlyStatics(nameof(NonCollectibleArrayTypeInSharedGenericCode));

[Fact]
[SkipOnMono("PlatformDetection.IsPreciseGcSupported false on mono", TestPlatforms.Any)]
public static void TestCollectibleEmptyArray() => TestCollectibleReadOnlyStatics(nameof(CollectibleEmptyArray));

private static void TestCollectibleReadOnlyStatics(string methodName)
{
string assemblyPath = typeof(Runtime_100437).Assembly.Location;

// Skip this test for single file
if (string.IsNullOrEmpty(assemblyPath))
return;

WeakReference wr = CreateReadOnlyStaticWeakReference();

for (int i = 0; i < 10; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();

if (!IsTargetAlive(wr))
return;
}

throw new Exception("Test failed - readonly static has not been collected.");

[MethodImpl(MethodImplOptions.NoInlining)]
WeakReference CreateReadOnlyStaticWeakReference()
{
AssemblyLoadContext alc = new CollectibleAssemblyLoadContext();
Assembly a = alc.LoadFromAssemblyPath(assemblyPath);
return (WeakReference)a.GetType(nameof(Runtime_100437)).GetMethod(methodName).Invoke(null, new object[] { typeof(Runtime_100437).Assembly });
}

[MethodImpl(MethodImplOptions.NoInlining)]
bool IsTargetAlive(WeakReference wr)
{
return wr.Target != null;
}
}

public static WeakReference NonCollectibleType(Assembly assemblyInDefaultContext)
{
return new WeakReference(Holder.Singleton, trackResurrection: true);
}

public static WeakReference NonCollectibleTypeInSharedGenericCode(Assembly assemblyInDefaultContext)
{
// Create instance of a non-collectible generic type definition over a collectible type
var type = assemblyInDefaultContext.GetType("Runtime_100437+GenericHolder`1", throwOnError: true).MakeGenericType(typeof(Runtime_100437));
var field = type.GetField("Singleton", BindingFlags.Static | BindingFlags.Public);
return new WeakReference(field.GetValue(null), trackResurrection: true);
}

public static WeakReference NonCollectibleArrayTypeInSharedGenericCode(Assembly assemblyInDefaultContext)
{
// Create instance of a non-collectible generic type definition over a collectible type
var type = assemblyInDefaultContext.GetType("Runtime_100437+GenericArrayHolder`1", throwOnError: true).MakeGenericType(typeof(Runtime_100437));
var field = type.GetField("Singleton", BindingFlags.Static | BindingFlags.Public);
return new WeakReference(field.GetValue(null), trackResurrection: true);
}

public static WeakReference CollectibleEmptyArray(Assembly assemblyInDefaultContext)
{
return new WeakReference(Array.Empty<Runtime_100437>(), trackResurrection: true);
}

private class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext()
: base(isCollectible: true)
{
}
}

private class Holder
{
public static readonly object Singleton = new object();
}

private class GenericHolder<T>
{
public static readonly object Singleton = new object();
}

private class GenericArrayHolder<T>
{
public static readonly int[] Singleton = new int[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>

0 comments on commit 7ae2d00

Please sign in to comment.