Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 635a609

Browse files
filipnavarajkotas
authored andcommitted
Move MemoryFailPoint to shared CoreLib partition (#22104)
* Move MemoryFailPoint to shared CoreLib partition. * Split MemoryFailPoint into Unix and Windows versions. * Replace MemoryFailPoint.GetMemorySettings FCall with GC.GetSegmentSize to make sharing with CoreRT easier.
1 parent 61ff739 commit 635a609

20 files changed

+339
-250
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@
260260
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\SafeHandle.cs" />
261261
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.cs" />
262262
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyDependencyResolver.cs" />
263-
<Compile Include="$(BclSourcesRoot)\System\Runtime\MemoryFailPoint.cs" />
264263
<Compile Include="$(BclSourcesRoot)\System\Runtime\Serialization\FormatterServices.cs" />
265264
<Compile Include="$(BclSourcesRoot)\System\Runtime\Versioning\CompatibilitySwitch.cs" />
266265
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
unsafe internal static bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer)
13+
{
14+
buffer.length = sizeof(MEMORYSTATUSEX);
15+
return GlobalMemoryStatusExNative(ref buffer);
16+
}
17+
18+
[DllImport(Libraries.Kernel32, SetLastError = true, EntryPoint = "GlobalMemoryStatusEx")]
19+
private static extern bool GlobalMemoryStatusExNative(ref MEMORYSTATUSEX buffer);
20+
}
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
[StructLayout(LayoutKind.Sequential)]
13+
internal struct MEMORYSTATUSEX
14+
{
15+
// The length field must be set to the size of this data structure.
16+
internal int length;
17+
internal int memoryLoad;
18+
internal ulong totalPhys;
19+
internal ulong availPhys;
20+
internal ulong totalPageFile;
21+
internal ulong availPageFile;
22+
internal ulong totalVirtual;
23+
internal ulong availVirtual;
24+
internal ulong availExtendedVirtual;
25+
}
26+
}
27+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
[StructLayout(LayoutKind.Sequential)]
13+
internal unsafe struct MEMORY_BASIC_INFORMATION
14+
{
15+
internal void* BaseAddress;
16+
internal void* AllocationBase;
17+
internal uint AllocationProtect;
18+
internal UIntPtr RegionSize;
19+
internal uint State;
20+
internal uint Protect;
21+
internal uint Type;
22+
}
23+
}
24+
}
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
internal const int MEM_COMMIT = 0x1000;
13+
internal const int MEM_RESERVE = 0x2000;
14+
internal const int MEM_RELEASE = 0x8000;
15+
internal const int MEM_FREE = 0x10000;
16+
internal const int PAGE_READWRITE = 0x04;
17+
18+
[DllImport(Libraries.Kernel32)]
19+
internal static extern unsafe void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode);
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
[DllImport(Libraries.Kernel32)]
13+
unsafe internal static extern bool VirtualFree(void* address, UIntPtr numBytes, int pageFreeMode);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
[DllImport(Libraries.Kernel32, SetLastError = true)]
13+
unsafe internal static extern UIntPtr VirtualQuery(void* address, ref MEMORY_BASIC_INFORMATION buffer, UIntPtr sizeOfBuffer);
14+
}
15+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@
672672
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\StreamingContext.cs" />
673673
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\NonVersionableAttribute.cs" />
674674
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\TargetFrameworkAttribute.cs" />
675+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.cs" />
675676
<Compile Include="$(MSBuildThisFileDirectory)System\SByte.cs" />
676677
<Compile Include="$(MSBuildThisFileDirectory)System\Security\AllowPartiallyTrustedCallersAttribute.cs" />
677678
<Compile Include="$(MSBuildThisFileDirectory)System\Security\CryptographicException.cs" />
@@ -926,11 +927,15 @@
926927
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs" />
927928
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" />
928929
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" />
930+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />
929931
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempFileNameW.cs" />
930932
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs" />
931933
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" />
934+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs" />
932935
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs" />
933936
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" />
937+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MEMORY_BASIC_INFORMATION.cs" />
938+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MEMORYSTATUSEX.cs" />
934939
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MultiByteToWideChar.cs" />
935940
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.OutputDebugString.cs" />
936941
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" />
@@ -942,7 +947,11 @@
942947
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs" />
943948
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" />
944949
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs" />
950+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SYSTEM_INFO.cs" />
945951
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.cs" />
952+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualAlloc.cs" />
953+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualFree.cs" />
954+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualQuery.cs" />
946955
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WideCharToMultiByte.cs" />
947956
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_IntPtr.cs" />
948957
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs" />
@@ -975,6 +984,7 @@
975984
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
976985
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
977986
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
987+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
978988
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
979989
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
980990
</ItemGroup>
@@ -1082,6 +1092,7 @@
10821092
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs" />
10831093
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
10841094
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
1095+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Unix.cs" />
10851096
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
10861097
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
10871098
</ItemGroup>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
namespace System.Runtime
6+
{
7+
public sealed partial class MemoryFailPoint
8+
{
9+
private static ulong GetTopOfMemory()
10+
{
11+
// These values are optimistic assumptions. In reality the value will
12+
// often be lower.
13+
return IntPtr.Size == 4 ? uint.MaxValue : ulong.MaxValue;
14+
}
15+
16+
private static bool CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree)
17+
{
18+
// TODO: Implement
19+
availPageFile = 0;
20+
totalAddressSpaceFree = 0;
21+
return false;
22+
}
23+
24+
// Based on the shouldThrow parameter, this will throw an exception, or
25+
// returns whether there is enough space. In all cases, we update
26+
// our last known free address space, hopefully avoiding needing to
27+
// probe again.
28+
private static bool CheckForFreeAddressSpace(ulong size, bool shouldThrow)
29+
{
30+
// Unreachable until CheckForAvailableMemory is implemented
31+
return false;
32+
}
33+
34+
// Allocate a specified number of bytes, commit them and free them. This should enlarge
35+
// page file if necessary and possible.
36+
private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes)
37+
{
38+
// Unreachable until CheckForAvailableMemory is implemented
39+
}
40+
}
41+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
// See the LICENSE file in the project root for more information.
4+
5+
using System.IO;
6+
using System.Runtime.InteropServices;
7+
8+
namespace System.Runtime
9+
{
10+
public sealed partial class MemoryFailPoint
11+
{
12+
private static ulong GetTopOfMemory()
13+
{
14+
Interop.Kernel32.SYSTEM_INFO info = new Interop.Kernel32.SYSTEM_INFO();
15+
Interop.Kernel32.GetSystemInfo(out info);
16+
return (ulong)info.lpMaximumApplicationAddress;
17+
}
18+
19+
private static bool CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree)
20+
{
21+
bool r;
22+
Interop.Kernel32.MEMORYSTATUSEX memory = new Interop.Kernel32.MEMORYSTATUSEX();
23+
r = Interop.Kernel32.GlobalMemoryStatusEx(ref memory);
24+
if (!r)
25+
throw Win32Marshal.GetExceptionForLastWin32Error();
26+
availPageFile = memory.availPageFile;
27+
totalAddressSpaceFree = memory.availVirtual;
28+
// Console.WriteLine($"Memory gate: Mem load: {memory.memoryLoad}% Available memory (physical + page file): {(memory.availPageFile >> 20)} MB Total free address space: {memory.availVirtual >> 20} MB GC Heap: {(GC.GetTotalMemory(true) >> 20)} MB");
29+
return true;
30+
}
31+
32+
// Based on the shouldThrow parameter, this will throw an exception, or
33+
// returns whether there is enough space. In all cases, we update
34+
// our last known free address space, hopefully avoiding needing to
35+
// probe again.
36+
private static unsafe bool CheckForFreeAddressSpace(ulong size, bool shouldThrow)
37+
{
38+
// Start walking the address space at 0. VirtualAlloc may wrap
39+
// around the address space. We don't need to find the exact
40+
// pages that VirtualAlloc would return - we just need to
41+
// know whether VirtualAlloc could succeed.
42+
ulong freeSpaceAfterGCHeap = MemFreeAfterAddress(null, size);
43+
44+
// Console.WriteLine($"MemoryFailPoint: Checked for free VA space. Found enough? {(freeSpaceAfterGCHeap >= size)} Asked for: {size} Found: {freeSpaceAfterGCHeap}");
45+
46+
// We may set these without taking a lock - I don't believe
47+
// this will hurt, as long as we never increment this number in
48+
// the Dispose method. If we do an extra bit of checking every
49+
// once in a while, but we avoid taking a lock, we may win.
50+
LastKnownFreeAddressSpace = (long)freeSpaceAfterGCHeap;
51+
LastTimeCheckingAddressSpace = Environment.TickCount;
52+
53+
if (freeSpaceAfterGCHeap < size && shouldThrow)
54+
throw new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint_VAFrag);
55+
return freeSpaceAfterGCHeap >= size;
56+
}
57+
58+
// Returns the amount of consecutive free memory available in a block
59+
// of pages. If we didn't have enough address space, we still return
60+
// a positive value < size, to help potentially avoid the overhead of
61+
// this check if we use a MemoryFailPoint with a smaller size next.
62+
private static unsafe ulong MemFreeAfterAddress(void* address, ulong size)
63+
{
64+
if (size >= s_topOfMemory)
65+
return 0;
66+
67+
ulong largestFreeRegion = 0;
68+
Interop.Kernel32.MEMORY_BASIC_INFORMATION memInfo = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
69+
UIntPtr sizeOfMemInfo = (UIntPtr)sizeof(Interop.Kernel32.MEMORY_BASIC_INFORMATION);
70+
71+
while (((ulong)address) + size < s_topOfMemory)
72+
{
73+
UIntPtr r = Interop.Kernel32.VirtualQuery(address, ref memInfo, sizeOfMemInfo);
74+
if (r == UIntPtr.Zero)
75+
throw Win32Marshal.GetExceptionForLastWin32Error();
76+
77+
ulong regionSize = memInfo.RegionSize.ToUInt64();
78+
if (memInfo.State == Interop.Kernel32.MEM_FREE)
79+
{
80+
if (regionSize >= size)
81+
return regionSize;
82+
else
83+
largestFreeRegion = Math.Max(largestFreeRegion, regionSize);
84+
}
85+
address = (void*)((ulong)address + regionSize);
86+
}
87+
return largestFreeRegion;
88+
}
89+
90+
// Allocate a specified number of bytes, commit them and free them. This should enlarge
91+
// page file if necessary and possible.
92+
private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes)
93+
{
94+
unsafe
95+
{
96+
#if ENABLE_WINRT
97+
void* pMemory = Interop.mincore.VirtualAllocFromApp(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
98+
#else
99+
void* pMemory = Interop.Kernel32.VirtualAlloc(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
100+
#endif
101+
if (pMemory != null)
102+
{
103+
bool r = Interop.Kernel32.VirtualFree(pMemory, UIntPtr.Zero, Interop.Kernel32.MEM_RELEASE);
104+
if (!r)
105+
throw Win32Marshal.GetExceptionForLastWin32Error();
106+
}
107+
}
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)