Remove freed memory range from tree on memory block disposal (#3347)
* Remove freed memory range from tree on memory block disposal * PR feedback
This commit is contained in:
parent
633c5ec330
commit
dd8f97ab9e
4 changed files with 76 additions and 21 deletions
|
@ -19,6 +19,8 @@ namespace Ryujinx.Memory
|
||||||
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
|
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
|
||||||
private int _viewCount;
|
private int _viewCount;
|
||||||
|
|
||||||
|
internal bool ForceWindows4KBView => _forceWindows4KBView;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pointer to the memory block data.
|
/// Pointer to the memory block data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -145,7 +147,7 @@ namespace Ryujinx.Memory
|
||||||
srcBlock.IncrementViewCount();
|
srcBlock.IncrementViewCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView);
|
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -156,7 +158,7 @@ namespace Ryujinx.Memory
|
||||||
/// <param name="size">Size of the range to be unmapped</param>
|
/// <param name="size">Size of the range to be unmapped</param>
|
||||||
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
|
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
|
||||||
{
|
{
|
||||||
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView);
|
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -68,17 +68,17 @@ namespace Ryujinx.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, bool force4KBMap)
|
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
if (force4KBMap)
|
if (owner.ForceWindows4KBView)
|
||||||
{
|
{
|
||||||
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
|
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
|
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
@ -91,17 +91,17 @@ namespace Ryujinx.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, bool force4KBMap)
|
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
if (force4KBMap)
|
if (owner.ForceWindows4KBView)
|
||||||
{
|
{
|
||||||
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
|
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
|
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
|
|
@ -68,9 +68,9 @@ namespace Ryujinx.Memory
|
||||||
return WindowsApi.VirtualFree(location, size, AllocationType.Decommit);
|
return WindowsApi.VirtualFree(location, size, AllocationType.Decommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
|
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
_placeholders.MapView(sharedMemory, srcOffset, location, size);
|
_placeholders.MapView(sharedMemory, srcOffset, location, size, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
|
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
|
||||||
|
@ -106,9 +106,9 @@ namespace Ryujinx.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
|
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
_placeholders.UnmapView(sharedMemory, location, size);
|
_placeholders.UnmapView(sharedMemory, location, size, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnmapView4KB(IntPtr location, IntPtr size)
|
public static void UnmapView4KB(IntPtr location, IntPtr size)
|
||||||
|
@ -154,7 +154,7 @@ namespace Ryujinx.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_placeholders.UnmapView(IntPtr.Zero, address, size);
|
_placeholders.UnreserveRange((ulong)address, (ulong)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);
|
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);
|
||||||
|
|
|
@ -44,6 +44,50 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Start address of the region to unreserve</param>
|
||||||
|
/// <param name="size">Size in bytes of the region to unreserve</param>
|
||||||
|
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception>
|
||||||
|
public void UnreserveRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
|
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||||
|
int count;
|
||||||
|
|
||||||
|
lock (_mappings)
|
||||||
|
{
|
||||||
|
count = _mappings.Get(address, endAddress, ref overlaps);
|
||||||
|
|
||||||
|
for (int index = 0; index < count; index++)
|
||||||
|
{
|
||||||
|
var overlap = overlaps[index];
|
||||||
|
|
||||||
|
if (IsMapped(overlap.Value))
|
||||||
|
{
|
||||||
|
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
|
||||||
|
{
|
||||||
|
throw new WindowsApiException("UnmapViewOfFile2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mappings.Remove(overlap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 1)
|
||||||
|
{
|
||||||
|
CheckFreeResult(WindowsApi.VirtualFree(
|
||||||
|
(IntPtr)address,
|
||||||
|
(IntPtr)size,
|
||||||
|
AllocationType.Release | AllocationType.CoalescePlaceholders));
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveProtection(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a shared memory view on a previously reserved memory region.
|
/// Maps a shared memory view on a previously reserved memory region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -51,13 +95,14 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
/// <param name="srcOffset">Offset in the shared memory to map</param>
|
/// <param name="srcOffset">Offset in the shared memory to map</param>
|
||||||
/// <param name="location">Address to map the view into</param>
|
/// <param name="location">Address to map the view into</param>
|
||||||
/// <param name="size">Size of the view in bytes</param>
|
/// <param name="size">Size of the view in bytes</param>
|
||||||
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
|
/// <param name="owner">Memory block that owns the mapping</param>
|
||||||
|
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
|
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UnmapViewInternal(sharedMemory, location, size);
|
UnmapViewInternal(sharedMemory, location, size, owner);
|
||||||
MapViewInternal(sharedMemory, srcOffset, location, size);
|
MapViewInternal(sharedMemory, srcOffset, location, size);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -173,13 +218,14 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
|
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
|
||||||
/// <param name="location">Address to unmap</param>
|
/// <param name="location">Address to unmap</param>
|
||||||
/// <param name="size">Size of the region to unmap in bytes</param>
|
/// <param name="size">Size of the region to unmap in bytes</param>
|
||||||
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
|
/// <param name="owner">Memory block that owns the mapping</param>
|
||||||
|
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
|
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UnmapViewInternal(sharedMemory, location, size);
|
UnmapViewInternal(sharedMemory, location, size, owner);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -197,8 +243,9 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
|
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
|
||||||
/// <param name="location">Address to unmap</param>
|
/// <param name="location">Address to unmap</param>
|
||||||
/// <param name="size">Size of the region to unmap in bytes</param>
|
/// <param name="size">Size of the region to unmap in bytes</param>
|
||||||
|
/// <param name="owner">Memory block that owns the mapping</param>
|
||||||
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
|
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
|
||||||
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size)
|
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
ulong startAddress = (ulong)location;
|
ulong startAddress = (ulong)location;
|
||||||
ulong unmapSize = (ulong)size;
|
ulong unmapSize = (ulong)size;
|
||||||
|
@ -272,7 +319,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoalesceForUnmap(startAddress, unmapSize);
|
CoalesceForUnmap(startAddress, unmapSize, owner);
|
||||||
RemoveProtection(startAddress, unmapSize);
|
RemoveProtection(startAddress, unmapSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,15 +328,21 @@ namespace Ryujinx.Memory.WindowsShared
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Address of the region that was unmapped</param>
|
/// <param name="address">Address of the region that was unmapped</param>
|
||||||
/// <param name="size">Size of the region that was unmapped in bytes</param>
|
/// <param name="size">Size of the region that was unmapped in bytes</param>
|
||||||
private void CoalesceForUnmap(ulong address, ulong size)
|
/// <param name="owner">Memory block that owns the mapping</param>
|
||||||
|
private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner)
|
||||||
{
|
{
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
ulong blockAddress = (ulong)owner.Pointer;
|
||||||
|
ulong blockEnd = blockAddress + owner.Size;
|
||||||
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
|
||||||
int unmappedCount = 0;
|
int unmappedCount = 0;
|
||||||
|
|
||||||
lock (_mappings)
|
lock (_mappings)
|
||||||
{
|
{
|
||||||
int count = _mappings.Get(address - MinimumPageSize, endAddress + MinimumPageSize, ref overlaps);
|
int count = _mappings.Get(
|
||||||
|
Math.Max(address - MinimumPageSize, blockAddress),
|
||||||
|
Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps);
|
||||||
|
|
||||||
if (count < 2)
|
if (count < 2)
|
||||||
{
|
{
|
||||||
// Nothing to coalesce if we only have 1 or no overlaps.
|
// Nothing to coalesce if we only have 1 or no overlaps.
|
||||||
|
|
Reference in a new issue