// 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. namespace System.Runtime.InteropServices { /// /// Handle for heap memory that allows tracking of capacity and reallocating. /// internal sealed class SafeHeapHandle : SafeBuffer { /// /// Allocate a buffer of the given size if requested. /// /// Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit. /// Thrown if the requested memory size cannot be allocated. /// Thrown if size is greater than the maximum memory size. public SafeHeapHandle(ulong byteLength) : base(ownsHandle: true) { Resize(byteLength); } public override bool IsInvalid { get { return handle == IntPtr.Zero; } } /// /// Resize the buffer to the given size if requested. /// /// Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit. /// Thrown if the requested memory size cannot be allocated. /// Thrown if size is greater than the maximum memory size. public void Resize(ulong byteLength) { if (IsClosed) throw new ObjectDisposedException(nameof(SafeHeapHandle)); ulong originalLength = 0; if (handle == IntPtr.Zero) { handle = Marshal.AllocHGlobal((IntPtr)byteLength); } else { originalLength = ByteLength; // This may or may not be the same handle, may realloc in place. If the // handle changes Windows will deal with the old handle, trying to free it will // cause an error. handle = Marshal.ReAllocHGlobal(pv: handle, cb: (IntPtr)byteLength); } if (handle == IntPtr.Zero) { // Only real plausible answer throw new OutOfMemoryException(); } if (byteLength > originalLength) { // Add pressure ulong addedBytes = byteLength - originalLength; if (addedBytes > long.MaxValue) { GC.AddMemoryPressure(long.MaxValue); GC.AddMemoryPressure((long)(addedBytes - long.MaxValue)); } else { GC.AddMemoryPressure((long)addedBytes); } } else { // Shrank or did nothing, release pressure if needed RemoveMemoryPressure(originalLength - byteLength); } Initialize(byteLength); } private void RemoveMemoryPressure(ulong removedBytes) { if (removedBytes == 0) return; if (removedBytes > long.MaxValue) { GC.RemoveMemoryPressure(long.MaxValue); GC.RemoveMemoryPressure((long)(removedBytes - long.MaxValue)); } else { GC.RemoveMemoryPressure((long)removedBytes); } } protected override bool ReleaseHandle() { if (handle != IntPtr.Zero) { RemoveMemoryPressure(ByteLength); Marshal.FreeHGlobal(handle); } handle = IntPtr.Zero; return true; } } }