From bdb62d04a7c7ea9bcea01b6314ba3306e07099f1 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 7 Sep 2024 12:50:58 -0400 Subject: minor memory api internal cleanup --- lib/Utils/src/Extensions/MemoryExtensions.cs | 9 ++-- lib/Utils/src/Memory/MemoryHandle.cs | 9 ++-- lib/Utils/src/Memory/MemoryUtil.cs | 6 +-- lib/Utils/src/Memory/MemoryUtilAlloc.cs | 62 ++++++++++++++++++++++++++++ lib/Utils/src/Memory/UnsafeMemoryHandle.cs | 31 ++++++++++---- lib/Utils/src/Memory/Win32PrivateHeap.cs | 61 +++++++++++++++------------ 6 files changed, 134 insertions(+), 44 deletions(-) (limited to 'lib/Utils/src') diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs index f2399a2..0cee73d 100644 --- a/lib/Utils/src/Extensions/MemoryExtensions.cs +++ b/lib/Utils/src/Extensions/MemoryExtensions.cs @@ -339,7 +339,8 @@ namespace VNLib.Utils.Extensions /// The new within the block /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SubSequence GetSubSequence(this IMemoryHandle block, nuint offset, int size) => new (block, offset, size); + public static SubSequence GetSubSequence(this IMemoryHandle block, nuint offset, int size) + => new (block, offset, size); /// /// Gets a window within the current block @@ -809,7 +810,8 @@ namespace VNLib.Utils.Extensions /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Obsolete("Functions are included directly on the type now")] - public static Span AsSpan(this in UnsafeMemoryHandle handle, int start) where T: unmanaged => handle.Span[start..]; + public static Span AsSpan(this in UnsafeMemoryHandle handle, int start) where T: unmanaged + => handle.Span[start..]; /// /// Creates a new sub-sequence over the target handle. (allows for convient sub span) @@ -822,7 +824,8 @@ namespace VNLib.Utils.Extensions /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Obsolete("Functions are included directly on the type now")] - public static Span AsSpan(this in UnsafeMemoryHandle handle, int start, int count) where T : unmanaged => handle.Span.Slice(start, count); + public static Span AsSpan(this in UnsafeMemoryHandle handle, int start, int count) where T : unmanaged + => handle.Span.Slice(start, count); /// /// Raises an if the current handle diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs index fbaae95..48e8193 100644 --- a/lib/Utils/src/Memory/MemoryHandle.cs +++ b/lib/Utils/src/Memory/MemoryHandle.cs @@ -206,7 +206,8 @@ namespace VNLib.Utils.Memory /// The reference to the item at the desired offset /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ref T GetOffsetRef(nuint offset) => ref Unsafe.AsRef(GetOffset(offset)); + public unsafe ref T GetOffsetRef(nuint offset) + => ref Unsafe.AsRef(GetOffset(offset)); /// /// @@ -256,9 +257,11 @@ namespace VNLib.Utils.Memory } /// - public override bool Equals(object? obj) => obj is MemoryHandle oHandle && Equals(oHandle); + public override bool Equals(object? obj) + => obj is MemoryHandle oHandle && Equals(oHandle); /// - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), handle.GetHashCode(), _length); + public override int GetHashCode() + => HashCode.Combine(base.GetHashCode(), handle.GetHashCode(), _length); } } \ No newline at end of file diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs index 09c36d5..1d3bccb 100644 --- a/lib/Utils/src/Memory/MemoryUtil.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -159,8 +159,6 @@ namespace VNLib.Utils.Memory private static IUnmangedHeap InitHeapInternal(bool isShared, bool enableStats, bool globalZero) { - bool IsWindows = OperatingSystem.IsWindows(); - //Get environment varable string? heapDllPath = Environment.GetEnvironmentVariable(SHARED_HEAP_FILE_PATH); string? rawFlagsEnv = Environment.GetEnvironmentVariable(SHARED_HEAP_RAW_FLAGS); @@ -195,8 +193,8 @@ namespace VNLib.Utils.Memory //Attempt to load the heap heap = NativeHeap.LoadHeap(heapDllPath, DllImportSearchPath.SafeDirectories, cFlags, userFlags); } - //No user heap was specified, use fallback - else if (IsWindows) + //No user heap was specified, use fallback on windows + else if (OperatingSystem.IsWindows()) { //We can use win32 heaps diff --git a/lib/Utils/src/Memory/MemoryUtilAlloc.cs b/lib/Utils/src/Memory/MemoryUtilAlloc.cs index 742346c..fb0f371 100644 --- a/lib/Utils/src/Memory/MemoryUtilAlloc.cs +++ b/lib/Utils/src/Memory/MemoryUtilAlloc.cs @@ -483,6 +483,68 @@ namespace VNLib.Utils.Memory return SafeAlloc(elements: (int)NearestPage(elements), zero); } + /// + /// DANGEROUS: Creates a new from an existing pointer that + /// belongs to the specified heap. This handle owns the pointer and will free it back + /// to the heap when disposed. No validation is performed on the pointer, so it is + /// the responsibility of the caller to ensure the pointer is valid and points to a + /// block of memory that was allocated from the specified heap. + /// + /// The pointer to the existing block of memory + /// The element size of the block (NOT byte size) + /// The unmanaged heap this block was allocated from (and can be freed back to) + /// A flag that indicates if block should be zeroed during reallocations + /// A wrapper + public static MemoryHandle ToHandle(nint blockPtr, nuint elements, IUnmangedHeap heap, bool zero) + where T : unmanaged + { + ArgumentNullException.ThrowIfNull(heap); + + return new MemoryHandle(heap, blockPtr, elements, zero); + } + + /// + /// DANGEROUS: Creates a new from an existing pointer that + /// belongs to the specified heap. This handle owns the pointer and will free it back + /// to the heap when disposed. No validation is performed on the pointer, so it is + /// the responsibility of the caller to ensure the pointer is valid and points to a + /// block of memory that was allocated from the specified heap. + /// + /// The pointer to the existing block of memory + /// The element size of the block (NOT byte size) + /// The unmanaged heap this block was allocated from (and can be freed back to) + /// A wrapper + public static UnsafeMemoryHandle ToUnsafeHandle(nint blockPtr, int elements, IUnmangedHeap heap) + where T : unmanaged + { + ArgumentNullException.ThrowIfNull(heap); + + return new UnsafeMemoryHandle(heap, blockPtr, elements); + } + + /// + /// Determines if two memory handles are equal by comparing their + /// lengths and their base block addresses. This method is useful + /// for comparing memory handles that may have been reallocated + /// or copied. + /// + /// + /// The first handle to compare + /// The secondary handle to compare against + /// + /// True if the handle lengths are equal and the base block addresses + /// are the same, otherwise false. + /// + /// + public static bool AreHandlesEqual(IMemoryHandle handle1, IMemoryHandle handle2) + { + ArgumentNullException.ThrowIfNull(handle1); + ArgumentNullException.ThrowIfNull(handle2); + + return handle1.Length == handle2.Length + && Unsafe.AreSame(ref handle1.GetReference(), ref handle2.GetReference()); + } + #endregion } diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs index 4041d0b..c968e7f 100644 --- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs +++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs @@ -253,11 +253,15 @@ namespace VNLib.Utils.Memory /// /// The other handle to test /// True if the other handle points to the same block of memory as the current handle + /// + /// Memory handles are the same if - The use the same underlying allocation type, handle size is the same + /// and the address of the block is the same. + /// public readonly bool Equals(in UnsafeMemoryHandle other) { return _handleType == other._handleType - && Length == other.Length - && GetHashCode() == other.GetHashCode(); + && _length == other._length + && Unsafe.AreSame(ref GetReference(), ref other.GetReference()); } /// @@ -266,7 +270,12 @@ namespace VNLib.Utils.Memory /// /// The other handle to test /// True if the other handle points to the same block of memory as the current handle - public readonly bool Equals(UnsafeMemoryHandle other) => Equals(in other); + /// + /// Memory handles are the same if - The use the same underlying allocation type, handle size is the same + /// and the address of the block is the same. + /// + public readonly bool Equals(UnsafeMemoryHandle other) + => Equals(in other); /// /// Override for object equality operator, will cause boxing @@ -275,10 +284,14 @@ namespace VNLib.Utils.Memory /// The other object to compare /// /// True if the passed object is of type - /// and uses the structure equality operator - /// false otherwise. + /// and uses the structure equality operator false otherwise. /// - public readonly override bool Equals([NotNullWhen(true)] object? obj) => obj is UnsafeMemoryHandle other && Equals(in other); + /// + /// Memory handles are the same if - The use the same underlying allocation type, handle size is the same + /// and the address of the block is the same. + /// + public readonly override bool Equals([NotNullWhen(true)] object? obj) + => obj is UnsafeMemoryHandle other && Equals(in other); /// /// Equality overload @@ -286,7 +299,8 @@ namespace VNLib.Utils.Memory /// /// /// True if handles are equal, flase otherwise - public static bool operator ==(in UnsafeMemoryHandle left, in UnsafeMemoryHandle right) => left.Equals(in right); + public static bool operator ==(in UnsafeMemoryHandle left, in UnsafeMemoryHandle right) + => left.Equals(in right); /// /// Equality overload @@ -294,7 +308,8 @@ namespace VNLib.Utils.Memory /// /// /// True if handles are equal, flase otherwise - public static bool operator !=(in UnsafeMemoryHandle left, in UnsafeMemoryHandle right) => !left.Equals(in right); + public static bool operator !=(in UnsafeMemoryHandle left, in UnsafeMemoryHandle right) + => !left.Equals(in right); } } diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs index d2ab201..b851dd2 100644 --- a/lib/Utils/src/Memory/Win32PrivateHeap.cs +++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs @@ -31,7 +31,7 @@ using System.Runtime.CompilerServices; using VNLib.Utils.Native; using DWORD = System.Int64; -using LPVOID = System.IntPtr; +using LPVOID = nint; namespace VNLib.Utils.Memory { @@ -112,37 +112,43 @@ namespace VNLib.Utils.Memory { if (cFlags.HasFlag(HeapCreation.Shared)) { - //Clear the synchronization flag because we don't need it for a process heap + /* + * When using the process-wide heap, synchronization is enabled + * so we should clear the flag to prevent UnmanagedHeapBase from + * using CLR mutual exclusion methods + */ cFlags &= ~HeapCreation.UseSynchronization; - //Get the process heap - LPVOID handle = GetProcessHeap(); - - //The heap does not own the handle - return new Win32PrivateHeap(handle, cFlags, false); + return new Win32PrivateHeap( + GetProcessHeap(), + cFlags, + ownsHeandle: false //The heap does not own the handle because it's shared, so it cannot be freed + ); } if (cFlags.HasFlag(HeapCreation.UseSynchronization)) { /* * When the synchronization flag is set, we dont need to use - * the win32 serialization method + * the win32 serialization method because the UnmanagedHeapBase + * class will handle locking using better/faster CLR methods */ flags |= HEAP_NO_SERIALIZE; } //Call create, throw exception if the heap falled to allocate - ERRNO heapHandle = HeapCreate(flags, initialSize, maxHeapSize); + ERRNO heapHandle = (ERRNO)HeapCreate(flags, initialSize, maxHeapSize); if (!heapHandle) { throw new NativeMemoryException("Heap could not be created"); } + Trace.WriteLine($"Win32 private heap {heapHandle:x} created"); //Heap has been created so we can wrap it - return new(heapHandle, cFlags, true); + return new(heapHandle, cFlags, ownsHeandle: true); } /// @@ -152,9 +158,11 @@ namespace VNLib.Utils.Memory /// An open and valid handle to a win32 private heap /// The heap creation flags to obey /// A wrapper around the specified heap - public static Win32PrivateHeap ConsumeExisting(IntPtr win32HeapHandle, HeapCreation flags) => new (win32HeapHandle, flags, true); + public static Win32PrivateHeap ConsumeExisting(IntPtr win32HeapHandle, HeapCreation flags) + => new (win32HeapHandle, flags, ownsHeandle: true); - private Win32PrivateHeap(IntPtr heapPtr, HeapCreation flags, bool ownsHeandle) : base(flags, ownsHeandle) => handle = heapPtr; + private Win32PrivateHeap(IntPtr heapPtr, HeapCreation flags, bool ownsHeandle) : base(flags, ownsHeandle) + => handle = heapPtr; /// /// Retrieves the size of a memory block allocated from the current heap. @@ -170,14 +178,12 @@ namespace VNLib.Utils.Memory /// True if the block is valid, false otherwise public bool Validate(ref LPVOID block) { - bool result; //Lock the heap before validating lock (HeapLock) { //validate the block on the current heap - result = HeapValidate(handle, HEAP_NO_FLAGS, block); + return HeapValidate(handle, HEAP_NO_FLAGS, block); } - return result; } /// @@ -188,15 +194,12 @@ namespace VNLib.Utils.Memory /// This can be a consuming operation which will block all allocations public bool Validate() { - bool result; - //Lock the heap before validating lock (HeapLock) { //validate the entire heap - result = HeapValidate(handle, HEAP_NO_FLAGS, IntPtr.Zero); + return HeapValidate(handle, HEAP_NO_FLAGS, IntPtr.Zero); } - return result; } /// @@ -211,22 +214,28 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override sealed LPVOID AllocBlock(nuint elements, nuint size, bool zero) { - nuint bytes = checked(elements * size); - - return HeapAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, bytes); + return HeapAlloc( + handle, + flags: zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, + dwBytes: checked(elements * size) + ); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override sealed bool FreeBlock(LPVOID block) => HeapFree(handle, HEAP_NO_FLAGS, block); + protected override sealed bool FreeBlock(LPVOID block) + => HeapFree(handle, HEAP_NO_FLAGS, block); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override sealed LPVOID ReAllocBlock(LPVOID block, nuint elements, nuint size, bool zero) { - nuint bytes = checked(elements * size); - - return HeapReAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, block, bytes); + return HeapReAlloc( + handle, + dwFlags: zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, + lpMem: block, + dwBytes: checked(elements * size) + ); } } } \ No newline at end of file -- cgit