diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Utils/src/Extensions/MemoryExtensions.cs | 9 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryHandle.cs | 9 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtil.cs | 6 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtilAlloc.cs | 62 | ||||
-rw-r--r-- | lib/Utils/src/Memory/UnsafeMemoryHandle.cs | 31 | ||||
-rw-r--r-- | lib/Utils/src/Memory/Win32PrivateHeap.cs | 61 |
6 files changed, 134 insertions, 44 deletions
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 /// <returns>The new <see cref="SubSequence{T}"/> within the block</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SubSequence<T> GetSubSequence<T>(this IMemoryHandle<T> block, nuint offset, int size) => new (block, offset, size); + public static SubSequence<T> GetSubSequence<T>(this IMemoryHandle<T> block, nuint offset, int size) + => new (block, offset, size); /// <summary> /// Gets a <see cref="SubSequence{T}"/> window within the current block @@ -809,7 +810,8 @@ namespace VNLib.Utils.Extensions /// <exception cref="ArgumentOutOfRangeException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] [Obsolete("Functions are included directly on the type now")] - public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start) where T: unmanaged => handle.Span[start..]; + public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start) where T: unmanaged + => handle.Span[start..]; /// <summary> /// Creates a new sub-sequence over the target handle. (allows for convient sub span) @@ -822,7 +824,8 @@ namespace VNLib.Utils.Extensions /// <exception cref="ArgumentOutOfRangeException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] [Obsolete("Functions are included directly on the type now")] - public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start, int count) where T : unmanaged => handle.Span.Slice(start, count); + public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start, int count) where T : unmanaged + => handle.Span.Slice(start, count); /// <summary> /// Raises an <see cref="ObjectDisposedException"/> 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 /// <returns>The reference to the item at the desired offset</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ref T GetOffsetRef(nuint offset) => ref Unsafe.AsRef<T>(GetOffset(offset)); + public unsafe ref T GetOffsetRef(nuint offset) + => ref Unsafe.AsRef<T>(GetOffset(offset)); ///<inheritdoc/> ///<exception cref="ObjectDisposedException"></exception> @@ -256,9 +257,11 @@ namespace VNLib.Utils.Memory } ///<inheritdoc/> - public override bool Equals(object? obj) => obj is MemoryHandle<T> oHandle && Equals(oHandle); + public override bool Equals(object? obj) + => obj is MemoryHandle<T> oHandle && Equals(oHandle); ///<inheritdoc/> - 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); } + /// <summary> + /// DANGEROUS: Creates a new <see cref="MemoryHandle{T}"/> 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. + /// </summary> + /// <param name="blockPtr">The pointer to the existing block of memory</param> + /// <param name="elements">The element size of the block (NOT byte size)</param> + /// <param name="heap">The unmanaged heap this block was allocated from (and can be freed back to)</param> + /// <param name="zero">A flag that indicates if block should be zeroed during reallocations</param> + /// <returns>A <see cref="MemoryHandle{T}"/> wrapper</returns> + public static MemoryHandle<T> ToHandle<T>(nint blockPtr, nuint elements, IUnmangedHeap heap, bool zero) + where T : unmanaged + { + ArgumentNullException.ThrowIfNull(heap); + + return new MemoryHandle<T>(heap, blockPtr, elements, zero); + } + + /// <summary> + /// DANGEROUS: Creates a new <see cref="MemoryHandle{T}"/> 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. + /// </summary> + /// <param name="blockPtr">The pointer to the existing block of memory</param> + /// <param name="elements">The element size of the block (NOT byte size)</param> + /// <param name="heap">The unmanaged heap this block was allocated from (and can be freed back to)</param> + /// <returns>A <see cref="MemoryHandle{T}"/> wrapper</returns> + public static UnsafeMemoryHandle<T> ToUnsafeHandle<T>(nint blockPtr, int elements, IUnmangedHeap heap) + where T : unmanaged + { + ArgumentNullException.ThrowIfNull(heap); + + return new UnsafeMemoryHandle<T>(heap, blockPtr, elements); + } + + /// <summary> + /// 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. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handle1">The first handle to compare</param> + /// <param name="handle2">The secondary handle to compare against</param> + /// <returns> + /// True if the handle lengths are equal and the base block addresses + /// are the same, otherwise false. + /// </returns> + /// <exception cref="ArgumentNullException"></exception> + public static bool AreHandlesEqual<T>(IMemoryHandle<T> handle1, IMemoryHandle<T> 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 /// </summary> /// <param name="other">The other handle to test</param> /// <returns>True if the other handle points to the same block of memory as the current handle</returns> + /// <remarks> + /// 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. + /// </remarks> public readonly bool Equals(in UnsafeMemoryHandle<T> other) { return _handleType == other._handleType - && Length == other.Length - && GetHashCode() == other.GetHashCode(); + && _length == other._length + && Unsafe.AreSame(ref GetReference(), ref other.GetReference()); } /// <summary> @@ -266,7 +270,12 @@ namespace VNLib.Utils.Memory /// </summary> /// <param name="other">The other handle to test</param> /// <returns>True if the other handle points to the same block of memory as the current handle</returns> - public readonly bool Equals(UnsafeMemoryHandle<T> other) => Equals(in other); + /// <remarks> + /// 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. + /// </remarks> + public readonly bool Equals(UnsafeMemoryHandle<T> other) + => Equals(in other); /// <summary> /// Override for object equality operator, will cause boxing @@ -275,10 +284,14 @@ namespace VNLib.Utils.Memory /// <param name="obj">The other object to compare</param> /// <returns> /// True if the passed object is of type <see cref="UnsafeMemoryHandle{T}"/> - /// and uses the structure equality operator - /// false otherwise. + /// and uses the structure equality operator false otherwise. /// </returns> - public readonly override bool Equals([NotNullWhen(true)] object? obj) => obj is UnsafeMemoryHandle<T> other && Equals(in other); + /// <remarks> + /// 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. + /// </remarks> + public readonly override bool Equals([NotNullWhen(true)] object? obj) + => obj is UnsafeMemoryHandle<T> other && Equals(in other); /// <summary> /// Equality overload @@ -286,7 +299,8 @@ namespace VNLib.Utils.Memory /// <param name="left"></param> /// <param name="right"></param> /// <returns>True if handles are equal, flase otherwise</returns> - public static bool operator ==(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => left.Equals(in right); + public static bool operator ==(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) + => left.Equals(in right); /// <summary> /// Equality overload @@ -294,7 +308,8 @@ namespace VNLib.Utils.Memory /// <param name="left"></param> /// <param name="right"></param> /// <returns>True if handles are equal, flase otherwise</returns> - public static bool operator !=(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => !left.Equals(in right); + public static bool operator !=(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> 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); } /// <summary> @@ -152,9 +158,11 @@ namespace VNLib.Utils.Memory /// <param name="win32HeapHandle">An open and valid handle to a win32 private heap</param> /// <param name="flags">The heap creation flags to obey</param> /// <returns>A wrapper around the specified heap</returns> - 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; /// <summary> /// Retrieves the size of a memory block allocated from the current heap. @@ -170,14 +178,12 @@ namespace VNLib.Utils.Memory /// <returns>True if the block is valid, false otherwise</returns> 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; } /// <summary> @@ -188,15 +194,12 @@ namespace VNLib.Utils.Memory /// <remarks>This can be a consuming operation which will block all allocations</remarks> 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; } ///<inheritdoc/> @@ -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) + ); } ///<inheritdoc/> [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); ///<inheritdoc/> [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 |