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

Commit

Permalink
Move MemoryFailPoint to shared CoreLib partition (#22104)
Browse files Browse the repository at this point in the history
* 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
filipnavara authored and jkotas committed Jan 22, 2019
1 parent 61ff739 commit 635a609
Show file tree
Hide file tree
Showing 20 changed files with 339 additions and 250 deletions.
1 change: 0 additions & 1 deletion src/System.Private.CoreLib/System.Private.CoreLib.csproj
Expand Up @@ -260,7 +260,6 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\SafeHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyDependencyResolver.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\MemoryFailPoint.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Serialization\FormatterServices.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Versioning\CompatibilitySwitch.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
Expand Down
@@ -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);
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
Expand Up @@ -672,6 +672,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\StreamingContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\NonVersionableAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\TargetFrameworkAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SByte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\AllowPartiallyTrustedCallersAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\CryptographicException.cs" />
Expand Down Expand Up @@ -926,11 +927,15 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempFileNameW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MEMORY_BASIC_INFORMATION.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MEMORYSTATUSEX.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MultiByteToWideChar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.OutputDebugString.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" />
Expand All @@ -942,7 +947,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SYSTEM_INFO.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualAlloc.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualFree.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualQuery.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WideCharToMultiByte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_IntPtr.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs" />
Expand Down Expand Up @@ -975,6 +984,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
</ItemGroup>
Expand Down Expand Up @@ -1082,6 +1092,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
</ItemGroup>
Expand Down
@@ -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
}
}
}
@@ -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();
}
}
}
}
}

0 comments on commit 635a609

Please sign in to comment.