From 54deded929203a64555d97424d5bb4b884fff69f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 5 May 2022 14:58:59 -0300 Subject: Fix shared memory leak on Windows (#3319) * Fix shared memory leak on Windows * Fix memory leak caused by RO session disposal not decrementing the memory manager ref count * Fix UnmapViewInternal deadlock * Was not supposed to add those back --- .../WindowsShared/PlaceholderManager4KB.cs | 170 +++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs (limited to 'Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs') diff --git a/Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs b/Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs new file mode 100644 index 00000000..fc056a2f --- /dev/null +++ b/Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs @@ -0,0 +1,170 @@ +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Memory.WindowsShared +{ + /// + /// Windows 4KB memory placeholder manager. + /// + [SupportedOSPlatform("windows")] + class PlaceholderManager4KB + { + private const int PageSize = MemoryManagementWindows.PageSize; + + private readonly IntervalTree _mappings; + + /// + /// Creates a new instance of the Windows 4KB memory placeholder manager. + /// + public PlaceholderManager4KB() + { + _mappings = new IntervalTree(); + } + + /// + /// Unmaps the specified range of memory and marks it as mapped internally. + /// + /// + /// Since this marks the range as mapped, the expectation is that the range will be mapped after calling this method. + /// + /// Memory address to unmap and mark as mapped + /// Size of the range in bytes + public void UnmapAndMarkRangeAsMapped(IntPtr location, IntPtr size) + { + ulong startAddress = (ulong)location; + ulong unmapSize = (ulong)size; + ulong endAddress = startAddress + unmapSize; + + var overlaps = Array.Empty>(); + int count = 0; + + lock (_mappings) + { + count = _mappings.Get(startAddress, endAddress, ref overlaps); + } + + for (int index = 0; index < count; index++) + { + var overlap = overlaps[index]; + + // Tree operations might modify the node start/end values, so save a copy before we modify the tree. + ulong overlapStart = overlap.Start; + ulong overlapEnd = overlap.End; + ulong overlapValue = overlap.Value; + + _mappings.Remove(overlap); + + ulong unmapStart = Math.Max(overlapStart, startAddress); + ulong unmapEnd = Math.Min(overlapEnd, endAddress); + + if (overlapStart < startAddress) + { + startAddress = overlapStart; + } + + if (overlapEnd > endAddress) + { + endAddress = overlapEnd; + } + + ulong currentAddress = unmapStart; + while (currentAddress < unmapEnd) + { + WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); + currentAddress += PageSize; + } + } + + _mappings.Add(startAddress, endAddress, 0); + } + + /// + /// Unmaps views at the specified memory range. + /// + /// Address of the range + /// Size of the range in bytes + public void UnmapView(IntPtr location, IntPtr size) + { + ulong startAddress = (ulong)location; + ulong unmapSize = (ulong)size; + ulong endAddress = startAddress + unmapSize; + + var overlaps = Array.Empty>(); + int count = 0; + + lock (_mappings) + { + count = _mappings.Get(startAddress, endAddress, ref overlaps); + } + + for (int index = 0; index < count; index++) + { + var overlap = overlaps[index]; + + // Tree operations might modify the node start/end values, so save a copy before we modify the tree. + ulong overlapStart = overlap.Start; + ulong overlapEnd = overlap.End; + + _mappings.Remove(overlap); + + if (overlapStart < startAddress) + { + _mappings.Add(overlapStart, startAddress, 0); + } + + if (overlapEnd > endAddress) + { + _mappings.Add(endAddress, overlapEnd, 0); + } + + ulong unmapStart = Math.Max(overlapStart, startAddress); + ulong unmapEnd = Math.Min(overlapEnd, endAddress); + + ulong currentAddress = unmapStart; + while (currentAddress < unmapEnd) + { + WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); + currentAddress += PageSize; + } + } + } + + /// + /// Unmaps mapped memory at a given range. + /// + /// Address of the range + /// Size of the range in bytes + public void UnmapRange(IntPtr location, IntPtr size) + { + ulong startAddress = (ulong)location; + ulong unmapSize = (ulong)size; + ulong endAddress = startAddress + unmapSize; + + var overlaps = Array.Empty>(); + int count = 0; + + lock (_mappings) + { + count = _mappings.Get(startAddress, endAddress, ref overlaps); + } + + for (int index = 0; index < count; index++) + { + var overlap = overlaps[index]; + + // Tree operations might modify the node start/end values, so save a copy before we modify the tree. + ulong unmapStart = Math.Max(overlap.Start, startAddress); + ulong unmapEnd = Math.Min(overlap.End, endAddress); + + _mappings.Remove(overlap); + + ulong currentAddress = unmapStart; + while (currentAddress < unmapEnd) + { + WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); + currentAddress += PageSize; + } + } + } + } +} \ No newline at end of file -- cgit v1.2.3