summaryrefslogtreecommitdiff
path: root/src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/MemoryFailPoint.Windows.cs110
1 files changed, 110 insertions, 0 deletions
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();
+ }
+ }
+ }
+ }
+}