summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj1
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs21
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORYSTATUSEX.cs27
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFORMATION.cs24
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualAlloc.cs21
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualFree.cs15
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualQuery.cs15
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems11
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Unix.cs41
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs110
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.cs (renamed from src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs)129
-rw-r--r--src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs70
-rw-r--r--src/System.Private.CoreLib/src/System/GC.cs3
-rw-r--r--src/vm/CMakeLists.txt2
-rw-r--r--src/vm/commemoryfailpoint.cpp43
-rw-r--r--src/vm/commemoryfailpoint.h28
-rw-r--r--src/vm/comutilnative.cpp20
-rw-r--r--src/vm/comutilnative.h1
-rw-r--r--src/vm/ecalllist.h6
-rw-r--r--src/vm/mscorlib.cpp1
20 files changed, 339 insertions, 250 deletions
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index b7c5623f6e..3d2eda1b53 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -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" />
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs
new file mode 100644
index 0000000000..6c56dba950
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs
@@ -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);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORYSTATUSEX.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORYSTATUSEX.cs
new file mode 100644
index 0000000000..45f57aa4d8
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORYSTATUSEX.cs
@@ -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;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFORMATION.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFORMATION.cs
new file mode 100644
index 0000000000..0744d53f66
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFORMATION.cs
@@ -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;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualAlloc.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualAlloc.cs
new file mode 100644
index 0000000000..18ba2a4940
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualAlloc.cs
@@ -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);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualFree.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualFree.cs
new file mode 100644
index 0000000000..dc3f42bc39
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualFree.cs
@@ -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);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualQuery.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualQuery.cs
new file mode 100644
index 0000000000..4cce807068
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.VirtualQuery.cs
@@ -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);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index faecf9bf34..4658c7fe67 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -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" />
@@ -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" />
@@ -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" />
@@ -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>
@@ -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>
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Unix.cs b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Unix.cs
new file mode 100644
index 0000000000..2f53052001
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Unix.cs
@@ -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
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs
new file mode 100644
index 0000000000..1e59a4ae5b
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs
@@ -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();
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.cs
index a6d8ab4284..88e222f318 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.cs
@@ -13,14 +13,10 @@
**
===========================================================*/
-using System;
using System.IO;
-using Microsoft.Win32;
-using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
-using System.Runtime.Versioning;
using System.Diagnostics;
/*
@@ -74,7 +70,7 @@ using System.Diagnostics;
namespace System.Runtime
{
- public sealed class MemoryFailPoint : CriticalFinalizerObject, IDisposable
+ public sealed partial class MemoryFailPoint : CriticalFinalizerObject, IDisposable
{
// Find the top section of user mode memory. Avoid the last 64K.
// Windows reserves that block for the kernel, apparently, and doesn't
@@ -82,7 +78,7 @@ namespace System.Runtime
// chunks, we don't have to special case this. Also, we need to
// deal with 32 bit machines in 3 GB mode.
// Using Win32's GetSystemInfo should handle all this for us.
- private static readonly ulong s_topOfMemory;
+ private static readonly ulong s_topOfMemory = GetTopOfMemory();
// Walking the address space is somewhat expensive, taking around half
// a millisecond. Doing that per transaction limits us to a max of
@@ -130,7 +126,7 @@ namespace System.Runtime
// Note: This may become dynamically tunable in the future.
// Also note that we can have different segment sizes for the normal vs.
// large object heap. We currently use the max of the two.
- private static readonly ulong s_GCSegmentSize;
+ private static readonly ulong s_GCSegmentSize = GC.GetSegmentSize();
// For multi-threaded workers, we want to ensure that if two workers
// use a MemoryFailPoint at the same time, and they both succeed, that
@@ -142,11 +138,6 @@ namespace System.Runtime
private ulong _reservedMemory; // The size of this request (from user)
private bool _mustSubtractReservation; // Did we add data to SharedStatics?
- static MemoryFailPoint()
- {
- GetMemorySettings(out s_GCSegmentSize, out s_topOfMemory);
- }
-
// We can remove this link demand in a future version - we will
// have scenarios for this in partial trust in the future, but
// we're doing this just to restrict this in case the code below
@@ -156,7 +147,6 @@ namespace System.Runtime
if (sizeInMegabytes <= 0)
throw new ArgumentOutOfRangeException(nameof(sizeInMegabytes), SR.ArgumentOutOfRange_NeedNonNegNum);
-#if !FEATURE_PAL // Remove this when CheckForAvailableMemory is able to provide legitimate estimates
ulong size = ((ulong)sizeInMegabytes) << 20;
_reservedMemory = size;
@@ -191,12 +181,16 @@ namespace System.Runtime
// would probably work, but do some thinking first.)
for (int stage = 0; stage < 3; stage++)
{
- CheckForAvailableMemory(out availPageFile, out totalAddressSpaceFree);
+ if (!CheckForAvailableMemory(out availPageFile, out totalAddressSpaceFree))
+ {
+ // _mustSubtractReservation == false
+ return;
+ }
// If we have enough room, then skip some stages.
// Note that multiple threads can still lead to a race condition for our free chunk
// of address space, which can't be easily solved.
- ulong reserved = (ulong)Volatile.Read(ref s_failPointReservedMemory);
+ ulong reserved = MemoryFailPointReservedMemory;
ulong segPlusReserved = segmentSize + reserved;
bool overflow = segPlusReserved < segmentSize || segPlusReserved < reserved;
bool needPageFile = availPageFile < (requestedSizeRounded + reserved + LowMemoryFudgeFactor) || overflow;
@@ -250,17 +244,7 @@ namespace System.Runtime
// This shouldn't overflow due to the if clauses above.
UIntPtr numBytes = new UIntPtr(segmentSize);
- unsafe
- {
- void* pMemory = Win32Native.VirtualAlloc(null, numBytes, Win32Native.MEM_COMMIT, Win32Native.PAGE_READWRITE);
- if (pMemory != null)
- {
- bool r = Win32Native.VirtualFree(pMemory, UIntPtr.Zero, Win32Native.MEM_RELEASE);
- if (!r)
- throw Win32Marshal.GetExceptionForLastWin32Error();
- }
- }
-
+ GrowPageFileIfNecessaryAndPossible(numBytes);
continue;
case 2:
@@ -307,84 +291,10 @@ namespace System.Runtime
RuntimeHelpers.PrepareConstrainedRegions();
- Interlocked.Add(ref s_failPointReservedMemory, (long)size);
+ AddMemoryFailPointReservation((long)size);
_mustSubtractReservation = true;
-#endif
- }
-
- private static void CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree)
- {
- bool r;
- Win32Native.MEMORYSTATUSEX memory = new Win32Native.MEMORYSTATUSEX();
- r = Win32Native.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");
- }
-
- // 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;
- Win32Native.MEMORY_BASIC_INFORMATION memInfo = new Win32Native.MEMORY_BASIC_INFORMATION();
- UIntPtr sizeOfMemInfo = (UIntPtr)Marshal.SizeOf(memInfo);
-
- while (((ulong)address) + size < s_topOfMemory)
- {
- UIntPtr r = Win32Native.VirtualQuery(address, ref memInfo, sizeOfMemInfo);
- if (r == UIntPtr.Zero)
- throw Win32Marshal.GetExceptionForLastWin32Error();
-
- ulong regionSize = memInfo.RegionSize.ToUInt64();
- if (memInfo.State == Win32Native.MEM_FREE)
- {
- if (regionSize >= size)
- return regionSize;
- else
- largestFreeRegion = Math.Max(largestFreeRegion, regionSize);
- }
- address = (void*)((ulong)address + regionSize);
- }
- return largestFreeRegion;
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void GetMemorySettings(out ulong maxGCSegmentSize, out ulong topOfMemory);
-
~MemoryFailPoint()
{
Dispose(false);
@@ -412,7 +322,7 @@ namespace System.Runtime
{
RuntimeHelpers.PrepareConstrainedRegions();
- Interlocked.Add(ref s_failPointReservedMemory, -(long)_reservedMemory);
+ AddMemoryFailPointReservation(-((long)_reservedMemory));
_mustSubtractReservation = false;
}
@@ -430,6 +340,21 @@ namespace System.Runtime
*/
}
+ internal static long AddMemoryFailPointReservation(long size)
+ {
+ // Size can legitimately be negative - see Dispose.
+ return Interlocked.Add(ref s_failPointReservedMemory, (long)size);
+ }
+
+ internal static ulong MemoryFailPointReservedMemory
+ {
+ get
+ {
+ Debug.Assert(Volatile.Read(ref s_failPointReservedMemory) >= 0, "Process-wide MemoryFailPoint reserved memory was negative!");
+ return (ulong)Volatile.Read(ref s_failPointReservedMemory);
+ }
+ }
+
#if DEBUG
[Serializable]
internal sealed class MemoryFailPointState
diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs
index 9207035d6e..d2007d8681 100644
--- a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs
+++ b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs
@@ -90,19 +90,8 @@
namespace Microsoft.Win32
{
using System;
- using System.Security;
- using System.Text;
- using System.Configuration.Assemblies;
using System.Runtime.InteropServices;
- using System.Threading;
using Microsoft.Win32.SafeHandles;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.Versioning;
-
- using BOOL = System.Int32;
- using DWORD = System.UInt32;
- using ULONG = System.UInt32;
/**
* Win32 encapsulation for System.Private.CoreLib.
@@ -129,33 +118,6 @@ namespace Microsoft.Win32
internal byte wReserved;
}
- [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;
- }
-
- [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;
- }
-
internal const string ADVAPI32 = "advapi32.dll";
[DllImport(Interop.Libraries.Kernel32, EntryPoint = "LocalAlloc")]
@@ -164,26 +126,8 @@ namespace Microsoft.Win32
[DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern IntPtr LocalFree(IntPtr handle);
- internal static bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer)
- {
- buffer.length = Marshal.SizeOf(typeof(MEMORYSTATUSEX));
- return GlobalMemoryStatusExNative(ref buffer);
- }
-
- [DllImport(Interop.Libraries.Kernel32, SetLastError = true, EntryPoint = "GlobalMemoryStatusEx")]
- private static extern bool GlobalMemoryStatusExNative([In, Out] ref MEMORYSTATUSEX buffer);
-
- [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
- internal static extern unsafe UIntPtr VirtualQuery(void* address, ref MEMORY_BASIC_INFORMATION buffer, UIntPtr sizeOfBuffer);
-
- // VirtualAlloc should generally be avoided, but is needed in
- // the MemoryFailPoint implementation (within a CER) to increase the
- // size of the page file, ignoring any host memory allocators.
- [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
- internal static extern unsafe void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode);
-
- [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
- internal static extern unsafe bool VirtualFree(void* address, UIntPtr numBytes, int pageFreeMode);
+ [DllImport(Interop.Libraries.Kernel32)]
+ internal static extern IntPtr LocalReAlloc(IntPtr handle, IntPtr sizetcbBytes, int uFlags);
[DllImport(Interop.Libraries.OleAut32, CharSet = CharSet.Unicode)]
internal static extern IntPtr SysAllocStringLen(string src, int len); // BSTR
@@ -213,13 +157,6 @@ namespace Microsoft.Win32
[DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle); // param is NOT a handle, but it returns one!
- internal const int PAGE_READWRITE = 0x04;
-
- internal const int MEM_COMMIT = 0x1000;
- internal const int MEM_RESERVE = 0x2000;
- internal const int MEM_RELEASE = 0x8000;
- internal const int MEM_FREE = 0x10000;
-
[DllImport(Interop.Libraries.Kernel32)]
internal static extern unsafe int WideCharToMultiByte(uint cp, uint flags, char* pwzSource, int cchSource, byte* pbDestBuffer, int cbDestBuffer, IntPtr null1, IntPtr null2);
@@ -261,9 +198,6 @@ namespace Microsoft.Win32
[DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
internal static extern uint ExpandEnvironmentStringsW(string lpSrc, ref char lpDst, uint nSize);
- [DllImport(Interop.Libraries.Kernel32)]
- internal static extern IntPtr LocalReAlloc(IntPtr handle, IntPtr sizetcbBytes, int uFlags);
-
[DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool QueryUnbiasedInterruptTime(out ulong UnbiasedTime);
diff --git a/src/System.Private.CoreLib/src/System/GC.cs b/src/System.Private.CoreLib/src/System/GC.cs
index aac612a63a..838e8147f2 100644
--- a/src/System.Private.CoreLib/src/System/GC.cs
+++ b/src/System.Private.CoreLib/src/System/GC.cs
@@ -104,6 +104,9 @@ namespace System
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern bool IsServerGC();
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern ulong GetSegmentSize();
+
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void _AddMemoryPressure(ulong bytesAllocated);
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index b3137d3e45..62acecf11e 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -298,7 +298,6 @@ set(VM_SOURCES_WKS
comdatetime.cpp
comdependenthandle.cpp
comdynamic.cpp
- commemoryfailpoint.cpp
commodule.cpp
compatibilityswitch.cpp
comsynchronizable.cpp
@@ -414,7 +413,6 @@ set(VM_HEADERS_WKS
comdatetime.h
comdependenthandle.h
comdynamic.h
- commemoryfailpoint.h
commodule.h
compatibilityswitch.h
comsynchronizable.h
diff --git a/src/vm/commemoryfailpoint.cpp b/src/vm/commemoryfailpoint.cpp
deleted file mode 100644
index 2900409fb1..0000000000
--- a/src/vm/commemoryfailpoint.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.
-
-/*============================================================
-**
-** Class: COMMemoryFailPoint
-**
-**
-** Purpose: Native methods for System.Runtime.MemoryFailPoint.
-** These are to implement memory gates to limit allocations
-** when progress will likely result in an OOM.
-**
-===========================================================*/
-#include "common.h"
-
-#include "frames.h"
-#include "commemoryfailpoint.h"
-
-// Need to know the maximum segment size for both the normal GC heap and the
-// large object heap, as well as the top user-accessible address within the
-// address space (ie, theoretically 2^31 - 1 on a 32 bit machine, but a tad
-// lower in practice). This will help out with 32 bit machines running in
-// 3 GB mode.
-FCIMPL2(void, COMMemoryFailPoint::GetMemorySettings, UINT64* pMaxGCSegmentSize, UINT64* pTopOfMemory)
-{
- FCALL_CONTRACT;
-
- IGCHeap * pGC = GCHeapUtilities::GetGCHeap();
- size_t segment_size = pGC->GetValidSegmentSize(false);
- size_t large_segment_size = pGC->GetValidSegmentSize(true);
- _ASSERTE(segment_size < SIZE_T_MAX && large_segment_size < SIZE_T_MAX);
- if (segment_size > large_segment_size)
- *pMaxGCSegmentSize = (UINT64) segment_size;
- else
- *pMaxGCSegmentSize = (UINT64) large_segment_size;
-
- // GetTopMemoryAddress returns a void*, which can't be cast
- // directly to a UINT64 without causing an error from GCC.
- void * topOfMem = GetTopMemoryAddress();
- *pTopOfMemory = (UINT64) (size_t) topOfMem;
-}
-FCIMPLEND
diff --git a/src/vm/commemoryfailpoint.h b/src/vm/commemoryfailpoint.h
deleted file mode 100644
index 902e5e36ed..0000000000
--- a/src/vm/commemoryfailpoint.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-/*============================================================
-**
-** Class: COMMemoryFailPoint
-**
-**
-** Purpose: Native methods for System.Runtime.MemoryFailPoint.
-** These are to implement memory gates to limit allocations
-** when progress will likely result in an OOM.
-**
-**
-===========================================================*/
-
-#ifndef _COMMEMORYFAILPOINT_H
-#define _COMMEMORYFAILPOINT_H
-
-#include "fcall.h"
-
-class COMMemoryFailPoint
-{
-public:
- static FCDECL2(void, GetMemorySettings, UINT64* pMaxGCSegmentSize, UINT64* pTopOfMemory);
-};
-
-#endif // _COMMEMORYFAILPOINT_H
diff --git a/src/vm/comutilnative.cpp b/src/vm/comutilnative.cpp
index 7ca48ffc59..56408c5f70 100644
--- a/src/vm/comutilnative.cpp
+++ b/src/vm/comutilnative.cpp
@@ -1034,6 +1034,26 @@ FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE)
}
FCIMPLEND
+/*================================GetSegmentSize========-=======================
+**Action: Returns the maximum GC heap segment size
+**Returns: The maximum segment size of either the normal heap or the large object heap, whichever is bigger
+==============================================================================*/
+FCIMPL0(UINT64, GCInterface::GetSegmentSize)
+{
+ FCALL_CONTRACT;
+
+ IGCHeap * pGC = GCHeapUtilities::GetGCHeap();
+ size_t segment_size = pGC->GetValidSegmentSize(false);
+ size_t large_segment_size = pGC->GetValidSegmentSize(true);
+ _ASSERTE(segment_size < SIZE_T_MAX && large_segment_size < SIZE_T_MAX);
+ if (segment_size < large_segment_size)
+ segment_size = large_segment_size;
+
+ FC_GC_POLL_RET();
+ return (UINT64) segment_size;
+}
+FCIMPLEND
+
/*================================CollectionCount=================================
**Action: Returns the number of collections for this generation since the begining of the life of the process
**Returns: The collection count.
diff --git a/src/vm/comutilnative.h b/src/vm/comutilnative.h
index 7f51e23db2..2b825b497a 100644
--- a/src/vm/comutilnative.h
+++ b/src/vm/comutilnative.h
@@ -120,6 +120,7 @@ public:
static FCDECL1(int, WaitForFullGCComplete, int millisecondsTimeout);
static FCDECL1(int, GetGenerationWR, LPVOID handle);
static FCDECL1(int, GetGeneration, Object* objUNSAFE);
+ static FCDECL0(UINT64, GetSegmentSize);
static
INT64 QCALLTYPE GetTotalMemory();
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index 5fce2000eb..83dadce185 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -786,6 +786,7 @@ FCFuncStart(gGCInterfaceFuncs)
QCFuncElement("_StartNoGCRegion", GCInterface::StartNoGCRegion)
QCFuncElement("_EndNoGCRegion", GCInterface::EndNoGCRegion)
FCFuncElement("IsServerGC", SystemNative::IsServerGC)
+ FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize)
QCFuncElement("_AddMemoryPressure", GCInterface::_AddMemoryPressure)
QCFuncElement("_RemoveMemoryPressure", GCInterface::_RemoveMemoryPressure)
FCFuncElement("GetGeneration", GCInterface::GetGeneration)
@@ -800,10 +801,6 @@ FCFuncStart(gGCInterfaceFuncs)
FCFuncElement("_GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread)
FCFuncEnd()
-FCFuncStart(gMemoryFailPointFuncs)
- FCFuncElement("GetMemorySettings", COMMemoryFailPoint::GetMemorySettings)
-FCFuncEnd()
-
FCFuncStart(gInteropMarshalFuncs)
FCFuncElement("GetLastWin32Error", MarshalNative::GetLastWin32Error)
FCFuncElement("SetLastWin32Error", MarshalNative::SetLastWin32Error)
@@ -1250,7 +1247,6 @@ FCClassElement("MathF", "System", gMathFFuncs)
FCClassElement("Mda", "System", gMda)
#endif
FCClassElement("MdUtf8String", "System", gMdUtf8String)
-FCClassElement("MemoryFailPoint", "System.Runtime", gMemoryFailPointFuncs)
FCClassElement("MetadataImport", "System.Reflection", gMetaDataImport)
FCClassElement("MissingMemberException", "System", gMissingMemberExceptionFuncs)
#ifdef FEATURE_COMINTEROP
diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp
index e69cfb8774..023a865e68 100644
--- a/src/vm/mscorlib.cpp
+++ b/src/vm/mscorlib.cpp
@@ -58,7 +58,6 @@
#include "reflectioninvocation.h"
#include "managedmdimport.hpp"
#include "synchronizationcontextnative.h"
-#include "commemoryfailpoint.h"
#include "typestring.h"
#include "comdependenthandle.h"
#include "weakreferencenative.h"