This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
61ff739
commit 635a609
Showing
20 changed files
with
339 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
unsafe internal static bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer) | ||
{ | ||
buffer.length = sizeof(MEMORYSTATUSEX); | ||
return GlobalMemoryStatusExNative(ref buffer); | ||
} | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true, EntryPoint = "GlobalMemoryStatusEx")] | ||
private static extern bool GlobalMemoryStatusExNative(ref MEMORYSTATUSEX buffer); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORYSTATUSEX.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct MEMORYSTATUSEX | ||
{ | ||
// The length field must be set to the size of this data structure. | ||
internal int length; | ||
internal int memoryLoad; | ||
internal ulong totalPhys; | ||
internal ulong availPhys; | ||
internal ulong totalPageFile; | ||
internal ulong availPageFile; | ||
internal ulong totalVirtual; | ||
internal ulong availVirtual; | ||
internal ulong availExtendedVirtual; | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...ystem.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFORMATION.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct MEMORY_BASIC_INFORMATION | ||
{ | ||
internal void* BaseAddress; | ||
internal void* AllocationBase; | ||
internal uint AllocationProtect; | ||
internal UIntPtr RegionSize; | ||
internal uint State; | ||
internal uint Protect; | ||
internal uint Type; | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualAlloc.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
internal const int MEM_COMMIT = 0x1000; | ||
internal const int MEM_RESERVE = 0x2000; | ||
internal const int MEM_RELEASE = 0x8000; | ||
internal const int MEM_FREE = 0x10000; | ||
internal const int PAGE_READWRITE = 0x04; | ||
|
||
[DllImport(Libraries.Kernel32)] | ||
internal static extern unsafe void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualFree.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
[DllImport(Libraries.Kernel32)] | ||
unsafe internal static extern bool VirtualFree(void* address, UIntPtr numBytes, int pageFreeMode); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal partial class Interop | ||
{ | ||
internal partial class Kernel32 | ||
{ | ||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
unsafe internal static extern UIntPtr VirtualQuery(void* address, ref MEMORY_BASIC_INFORMATION buffer, UIntPtr sizeOfBuffer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Unix.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace System.Runtime | ||
{ | ||
public sealed partial class MemoryFailPoint | ||
{ | ||
private static ulong GetTopOfMemory() | ||
{ | ||
// These values are optimistic assumptions. In reality the value will | ||
// often be lower. | ||
return IntPtr.Size == 4 ? uint.MaxValue : ulong.MaxValue; | ||
} | ||
|
||
private static bool CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree) | ||
{ | ||
// TODO: Implement | ||
availPageFile = 0; | ||
totalAddressSpaceFree = 0; | ||
return false; | ||
} | ||
|
||
// Based on the shouldThrow parameter, this will throw an exception, or | ||
// returns whether there is enough space. In all cases, we update | ||
// our last known free address space, hopefully avoiding needing to | ||
// probe again. | ||
private static bool CheckForFreeAddressSpace(ulong size, bool shouldThrow) | ||
{ | ||
// Unreachable until CheckForAvailableMemory is implemented | ||
return false; | ||
} | ||
|
||
// Allocate a specified number of bytes, commit them and free them. This should enlarge | ||
// page file if necessary and possible. | ||
private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes) | ||
{ | ||
// Unreachable until CheckForAvailableMemory is implemented | ||
} | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Runtime | ||
{ | ||
public sealed partial class MemoryFailPoint | ||
{ | ||
private static ulong GetTopOfMemory() | ||
{ | ||
Interop.Kernel32.SYSTEM_INFO info = new Interop.Kernel32.SYSTEM_INFO(); | ||
Interop.Kernel32.GetSystemInfo(out info); | ||
return (ulong)info.lpMaximumApplicationAddress; | ||
} | ||
|
||
private static bool CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree) | ||
{ | ||
bool r; | ||
Interop.Kernel32.MEMORYSTATUSEX memory = new Interop.Kernel32.MEMORYSTATUSEX(); | ||
r = Interop.Kernel32.GlobalMemoryStatusEx(ref memory); | ||
if (!r) | ||
throw Win32Marshal.GetExceptionForLastWin32Error(); | ||
availPageFile = memory.availPageFile; | ||
totalAddressSpaceFree = memory.availVirtual; | ||
// 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"); | ||
return true; | ||
} | ||
|
||
// Based on the shouldThrow parameter, this will throw an exception, or | ||
// returns whether there is enough space. In all cases, we update | ||
// our last known free address space, hopefully avoiding needing to | ||
// probe again. | ||
private static unsafe bool CheckForFreeAddressSpace(ulong size, bool shouldThrow) | ||
{ | ||
// Start walking the address space at 0. VirtualAlloc may wrap | ||
// around the address space. We don't need to find the exact | ||
// pages that VirtualAlloc would return - we just need to | ||
// know whether VirtualAlloc could succeed. | ||
ulong freeSpaceAfterGCHeap = MemFreeAfterAddress(null, size); | ||
|
||
// Console.WriteLine($"MemoryFailPoint: Checked for free VA space. Found enough? {(freeSpaceAfterGCHeap >= size)} Asked for: {size} Found: {freeSpaceAfterGCHeap}"); | ||
|
||
// We may set these without taking a lock - I don't believe | ||
// this will hurt, as long as we never increment this number in | ||
// the Dispose method. If we do an extra bit of checking every | ||
// once in a while, but we avoid taking a lock, we may win. | ||
LastKnownFreeAddressSpace = (long)freeSpaceAfterGCHeap; | ||
LastTimeCheckingAddressSpace = Environment.TickCount; | ||
|
||
if (freeSpaceAfterGCHeap < size && shouldThrow) | ||
throw new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint_VAFrag); | ||
return freeSpaceAfterGCHeap >= size; | ||
} | ||
|
||
// Returns the amount of consecutive free memory available in a block | ||
// of pages. If we didn't have enough address space, we still return | ||
// a positive value < size, to help potentially avoid the overhead of | ||
// this check if we use a MemoryFailPoint with a smaller size next. | ||
private static unsafe ulong MemFreeAfterAddress(void* address, ulong size) | ||
{ | ||
if (size >= s_topOfMemory) | ||
return 0; | ||
|
||
ulong largestFreeRegion = 0; | ||
Interop.Kernel32.MEMORY_BASIC_INFORMATION memInfo = new Interop.Kernel32.MEMORY_BASIC_INFORMATION(); | ||
UIntPtr sizeOfMemInfo = (UIntPtr)sizeof(Interop.Kernel32.MEMORY_BASIC_INFORMATION); | ||
|
||
while (((ulong)address) + size < s_topOfMemory) | ||
{ | ||
UIntPtr r = Interop.Kernel32.VirtualQuery(address, ref memInfo, sizeOfMemInfo); | ||
if (r == UIntPtr.Zero) | ||
throw Win32Marshal.GetExceptionForLastWin32Error(); | ||
|
||
ulong regionSize = memInfo.RegionSize.ToUInt64(); | ||
if (memInfo.State == Interop.Kernel32.MEM_FREE) | ||
{ | ||
if (regionSize >= size) | ||
return regionSize; | ||
else | ||
largestFreeRegion = Math.Max(largestFreeRegion, regionSize); | ||
} | ||
address = (void*)((ulong)address + regionSize); | ||
} | ||
return largestFreeRegion; | ||
} | ||
|
||
// Allocate a specified number of bytes, commit them and free them. This should enlarge | ||
// page file if necessary and possible. | ||
private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes) | ||
{ | ||
unsafe | ||
{ | ||
#if ENABLE_WINRT | ||
void* pMemory = Interop.mincore.VirtualAllocFromApp(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE); | ||
#else | ||
void* pMemory = Interop.Kernel32.VirtualAlloc(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE); | ||
#endif | ||
if (pMemory != null) | ||
{ | ||
bool r = Interop.Kernel32.VirtualFree(pMemory, UIntPtr.Zero, Interop.Kernel32.MEM_RELEASE); | ||
if (!r) | ||
throw Win32Marshal.GetExceptionForLastWin32Error(); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.