/// <summary> /// Unmaps mapped memory at a given range. /// </summary> /// <param name="location">Address of the range</param> /// <param name="size">Size of the range in bytes</param> public void UnmapRange(IntPtr location, IntPtr size) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty <IntervalTreeNode <ulong, byte> >(); 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; } } }
/// <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> /// Unmaps a view that has been previously mapped with <see cref="MapView"/>. /// </summary> /// <remarks> /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped. /// </remarks> /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param> /// <param name="location">Address to unmap</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> private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty <IntervalTreeNode <ulong, ulong> >(); int count; lock (_mappings) { count = _mappings.Get(startAddress, 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"); } // 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; lock (_mappings) { _mappings.Remove(overlap); _mappings.Add(overlapStart, overlapEnd, ulong.MaxValue); } bool overlapStartsBefore = overlapStart < startAddress; bool overlapEndsAfter = overlapEnd > endAddress; if (overlapStartsBefore || overlapEndsAfter) { // If the overlap extends beyond the region we are unmapping, // then we need to re-map the regions that are supposed to remain mapped. // This is necessary because Windows does not support partial view unmaps. // That is, you can only fully unmap a view that was previously mapped, you can't just unmap a chunck of it. LockCookie lockCookie = _partialUnmapLock.UpgradeToWriterLock(Timeout.Infinite); _partialUnmapsCount++; if (overlapStartsBefore) { ulong remapSize = startAddress - overlapStart; MapViewInternal(sharedMemory, overlapValue, (IntPtr)overlapStart, (IntPtr)remapSize); RestoreRangeProtection(overlapStart, remapSize); } if (overlapEndsAfter) { ulong overlappedSize = endAddress - overlapStart; ulong remapBackingOffset = overlapValue + overlappedSize; ulong remapAddress = overlapStart + overlappedSize; ulong remapSize = overlapEnd - endAddress; MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize); RestoreRangeProtection(remapAddress, remapSize); } _partialUnmapLock.DowngradeFromWriterLock(ref lockCookie); } } } CoalesceForUnmap(startAddress, unmapSize, owner); RemoveProtection(startAddress, unmapSize); }