Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@
using System;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

using Debug = System.Diagnostics.Debug;

// Rewrite of src\coreclr\vm\frozenobjectheap.cpp in C#

namespace Internal.Runtime
{
internal unsafe partial class FrozenObjectHeapManager
internal unsafe class FrozenObjectHeapManager
{
public static readonly FrozenObjectHeapManager Instance = new FrozenObjectHeapManager();

private readonly Lock m_Crst = new Lock(useTrivialWaits: true);
private FrozenObjectSegment m_CurrentSegment;

// Default size to reserve for a frozen segment
private const nuint FOH_SEGMENT_DEFAULT_SIZE = 4 * 1024 * 1024;
// Size to commit on demand in that reserved space
private const nuint FOH_COMMIT_SIZE = 64 * 1024;
private const nuint FOH_SEGMENT_DEFAULT_SIZE = 4 * 1024;

public T? TryAllocateObject<T>() where T : class
{
Expand All @@ -37,8 +33,6 @@ internal unsafe partial class FrozenObjectHeapManager
using (m_Crst.EnterScope())
{
Debug.Assert(type != null);
// _ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

// 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.
// (we currently never allocate them on frozen segments)
Expand All @@ -51,12 +45,10 @@ internal unsafe partial class FrozenObjectHeapManager
#endif

// NOTE: objectSize is expected be the full size including header
// _ASSERT(objectSize >= MIN_OBJECT_SIZE);

if (objectSize > FOH_COMMIT_SIZE)
if (objectSize > FOH_SEGMENT_DEFAULT_SIZE - 2 * (nuint)sizeof(ObjHeader))
{
// The current design doesn't allow objects larger than FOH_COMMIT_SIZE and
// since FrozenObjectHeap is just an optimization, let's not fill it with huge objects.
// Leave room for the first and next object headers in a default-sized segment.
return null;
}

Expand All @@ -67,7 +59,7 @@ internal unsafe partial class FrozenObjectHeapManager
nuint newSegmentSize = FOH_SEGMENT_DEFAULT_SIZE;
if (m_CurrentSegment != null)
{
// Double the reserved size to reduce the number of frozen segments in apps with lots of frozen objects
// Double the size to reduce the number of frozen segments in apps with lots of frozen objects
// Use the same size in case if prevSegmentSize*2 operation overflows.
nuint prevSegmentSize = m_CurrentSegment.m_Size;
newSegmentSize = Math.Max(prevSegmentSize, prevSegmentSize * 2);
Expand All @@ -90,21 +82,14 @@ internal unsafe partial class FrozenObjectHeapManager

private class FrozenObjectSegment
{
// Start of the reserved memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT)
// Start of the allocated memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT)
private byte* m_pStart;

// Pointer to the end of the current segment, ready to be used as a pMT for a new object
// meaning that "m_pCurrent - sizeof(ObjHeader)" is the actual start of the new object (header).
//
// m_pCurrent <= m_SizeCommitted
public byte* m_pCurrent;

// Memory committed in the current segment
//
// m_SizeCommitted <= m_pStart + FOH_SIZE_RESERVED
public nuint m_SizeCommitted;

// Total memory reserved for the current segment
// Total memory allocated for the current segment
public nuint m_Size;

private IntPtr m_SegmentHandle;
Expand All @@ -113,53 +98,23 @@ public FrozenObjectSegment(nuint sizeHint)
{
m_Size = sizeHint;

Debug.Assert(m_Size > FOH_COMMIT_SIZE);
Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0);
Debug.Assert(m_Size >= FOH_SEGMENT_DEFAULT_SIZE);

void* alloc = ClrVirtualReserve(m_Size);
if (alloc == null)
{
// Try again with the default FOH size
if (m_Size > FOH_SEGMENT_DEFAULT_SIZE)
{
m_Size = FOH_SEGMENT_DEFAULT_SIZE;
Debug.Assert(m_Size > FOH_COMMIT_SIZE);
Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0);
alloc = ClrVirtualReserve(m_Size);
}

if (alloc == null)
{
throw new OutOfMemoryException();
}
}

// Commit a chunk in advance
m_pStart = (byte*)ClrVirtualCommit(alloc, FOH_COMMIT_SIZE);
if (m_pStart == null)
{
ClrVirtualFree(alloc, m_Size);
throw new OutOfMemoryException();
}
m_pStart = (byte*)NativeMemory.AllocZeroed(m_Size);

m_pCurrent = m_pStart + sizeof(ObjHeader);

m_SegmentHandle = RuntimeImports.RhRegisterFrozenSegment(m_pStart, (nuint)m_pCurrent - (nuint)m_pStart, FOH_COMMIT_SIZE, m_Size);
m_SegmentHandle = RuntimeImports.RhRegisterFrozenSegment(m_pStart, (nuint)m_pCurrent - (nuint)m_pStart, m_Size, m_Size);
if (m_SegmentHandle == IntPtr.Zero)
{
ClrVirtualFree(alloc, m_Size);
NativeMemory.Free(m_pStart);
throw new OutOfMemoryException();
}

m_SizeCommitted = FOH_COMMIT_SIZE;
}

public HalfBakedObject* TryAllocateObject(MethodTable* type, nuint objectSize)
{
Debug.Assert((m_pStart != null) && (m_Size > 0));
//_ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT));
//_ASSERT(IS_ALIGNED(objectSize, DATA_ALIGNMENT));
Debug.Assert(objectSize <= FOH_COMMIT_SIZE);
Debug.Assert(m_pCurrent >= m_pStart + sizeof(ObjHeader));

nuint spaceUsed = (nuint)(m_pCurrent - m_pStart);
Expand All @@ -174,25 +129,12 @@ public FrozenObjectSegment(nuint sizeHint)
return null;
}

// Check if we need to commit a new chunk
if (spaceUsed + objectSize + (nuint)sizeof(ObjHeader) > m_SizeCommitted)
{
// Make sure we don't go out of bounds during this commit
Debug.Assert(m_SizeCommitted + FOH_COMMIT_SIZE <= m_Size);

if (ClrVirtualCommit(m_pStart + m_SizeCommitted, FOH_COMMIT_SIZE) == null)
{
throw new OutOfMemoryException();
}
m_SizeCommitted += FOH_COMMIT_SIZE;
}

HalfBakedObject* obj = (HalfBakedObject*)m_pCurrent;
obj->SetMethodTable(type);

m_pCurrent += objectSize;

RuntimeImports.RhUpdateFrozenSegment(m_SegmentHandle, m_pCurrent, m_pStart + m_SizeCommitted);
RuntimeImports.RhUpdateFrozenSegment(m_SegmentHandle, m_pCurrent, m_pStart + m_Size);

return obj;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@
<Link>Common\Interop\Windows\Kernel32\Interop.ExitProcess.cs</Link>
</Compile>
<Compile Include="System\EventReporter.cs" />
<Compile Include="Internal\Runtime\FrozenObjectHeapManager.Windows.cs" />
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.Windows.cs" />
<Compile Include="$(CommonPath)\System\Runtime\InteropServices\BuiltInVariantExtensions.cs">
<Link>System\Runtime\InteropServices\BuiltInVariantExtensions.cs</Link>
Expand Down Expand Up @@ -301,7 +300,6 @@
<Compile Include="System\Threading\ThreadPoolCallbackWrapper.NativeAot.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)'=='true' or '$(TargetsWasm)'=='true'">
<Compile Include="Internal\Runtime\FrozenObjectHeapManager.Unix.cs" />
<Compile Include="System\Environment.NativeAot.Unix.cs" />
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.Unix.cs" />
<Compile Include="System\Threading\Thread.NativeAot.Unix.cs" />
Expand All @@ -311,9 +309,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Exit.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Exit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MProtect.cs">
<Link>Common\Interop\Unix\System.Native\Interop.MProtect.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="$(CompilerCommonPath)\System\Collections\Generic\ArrayBuilder.cs">
Expand Down

This file was deleted.

32 changes: 32 additions & 0 deletions src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,42 @@ private static int Main()
TestActivatingThingsInSignature.Run();
TestArrayInitialize.Run();
TestDelegateInvokeFromEvent.Run();
TestFrozenRuntimeTypes.Run();

return 100;
}

class TestFrozenRuntimeTypes
{
public static void Run()
{
const int MaxRank = 32;
Type[] types = new Type[MaxRank * MaxRank];
for (int outerRank = 1; outerRank <= MaxRank; outerRank++)
{
for (int innerRank = 1; innerRank <= MaxRank; innerRank++)
{
Type type = typeof(TestFrozenRuntimeTypes).MakeArrayType(innerRank).MakeArrayType(outerRank);
types[(outerRank - 1) * MaxRank + innerRank - 1] = type;
}
}

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true);

for (int outerRank = 1; outerRank <= MaxRank; outerRank++)
{
for (int innerRank = 1; innerRank <= MaxRank; innerRank++)
{
Type type = types[(outerRank - 1) * MaxRank + innerRank - 1];
Assert.Equal(outerRank, type.GetArrayRank());
Assert.Equal(innerRank, type.GetElementType().GetArrayRank());
Assert.Same(typeof(TestFrozenRuntimeTypes), type.GetElementType().GetElementType());
Assert.Same(typeof(TestFrozenRuntimeTypes).MakeArrayType(innerRank).MakeArrayType(outerRank), type);
}
}
}
}

class TestReflectionInvoke
{
internal class InvokeTests
Expand Down
Loading