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 --- Ryujinx.Memory/WindowsShared/PlaceholderManager.cs | 15 +- .../WindowsShared/PlaceholderManager4KB.cs | 170 +++++++++++++++++++++ 2 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.Memory/WindowsShared/PlaceholderManager4KB.cs (limited to 'Ryujinx.Memory/WindowsShared') diff --git a/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs index b0b3bf05..d465f341 100644 --- a/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs +++ b/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Runtime.Versioning; using System.Threading; namespace Ryujinx.Memory.WindowsShared @@ -7,6 +8,7 @@ namespace Ryujinx.Memory.WindowsShared /// /// Windows memory placeholder manager. /// + [SupportedOSPlatform("windows")] class PlaceholderManager { private const ulong MinimumPageSize = 0x1000; @@ -203,7 +205,7 @@ namespace Ryujinx.Memory.WindowsShared ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty>(); - int count = 0; + int count; lock (_mappings) { @@ -226,8 +228,11 @@ namespace Ryujinx.Memory.WindowsShared ulong overlapEnd = overlap.End; ulong overlapValue = overlap.Value; - _mappings.Remove(overlap); - _mappings.Add(overlapStart, overlapEnd, ulong.MaxValue); + lock (_mappings) + { + _mappings.Remove(overlap); + _mappings.Add(overlapStart, overlapEnd, ulong.MaxValue); + } bool overlapStartsBefore = overlapStart < startAddress; bool overlapEndsAfter = overlapEnd > endAddress; @@ -364,7 +369,7 @@ namespace Ryujinx.Memory.WindowsShared ulong endAddress = reprotectAddress + reprotectSize; var overlaps = Array.Empty>(); - int count = 0; + int count; lock (_mappings) { @@ -534,7 +539,7 @@ namespace Ryujinx.Memory.WindowsShared { ulong endAddress = address + size; var overlaps = Array.Empty>(); - int count = 0; + int count; lock (_protections) { 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