aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-09-07 12:50:58 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-09-07 12:50:58 -0400
commitbdb62d04a7c7ea9bcea01b6314ba3306e07099f1 (patch)
tree54bcc69248a72b07bc771751305337eb117a1aab
parentd297b3a958e13a76ea61c8df588ec32ea9a40faf (diff)
minor memory api internal cleanup
-rw-r--r--apps/VNLib.WebServer/src/VLogProvider.cs28
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs9
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs9
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs6
-rw-r--r--lib/Utils/src/Memory/MemoryUtilAlloc.cs62
-rw-r--r--lib/Utils/src/Memory/UnsafeMemoryHandle.cs31
-rw-r--r--lib/Utils/src/Memory/Win32PrivateHeap.cs61
7 files changed, 143 insertions, 63 deletions
diff --git a/apps/VNLib.WebServer/src/VLogProvider.cs b/apps/VNLib.WebServer/src/VLogProvider.cs
index d20437f..67d88ef 100644
--- a/apps/VNLib.WebServer/src/VLogProvider.cs
+++ b/apps/VNLib.WebServer/src/VLogProvider.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.WebServer
@@ -35,14 +35,10 @@ using VNLib.Utils.Logging;
namespace VNLib.WebServer
{
- internal sealed class VLogProvider : VnDisposeable, ILogProvider
+ internal sealed class VLogProvider(LoggerConfiguration config) : VnDisposeable, ILogProvider
{
- private readonly Logger LogCore;
+ private readonly Logger LogCore = config.CreateLogger();
- public VLogProvider(LoggerConfiguration config)
- {
- LogCore = config.CreateLogger();
- }
public void Flush() { }
public object GetLogProvider() => LogCore;
@@ -51,22 +47,16 @@ namespace VNLib.WebServer
public bool IsEnabled(LogLevel level) => LogCore.IsEnabled((LogEventLevel)level);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Write(LogLevel level, string value)
- {
- LogCore.Write((LogEventLevel)level, value);
- }
+ public void Write(LogLevel level, string value)
+ => LogCore.Write((LogEventLevel)level, value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Write(LogLevel level, Exception exception, string value = "")
- {
- LogCore.Write((LogEventLevel)level, exception, value);
- }
+ public void Write(LogLevel level, Exception exception, string value = "")
+ => LogCore.Write((LogEventLevel)level, exception, value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Write(LogLevel level, string value, params object[] args)
- {
- LogCore.Write((LogEventLevel)level, value, args);
- }
+ public void Write(LogLevel level, string value, params object[] args)
+ => LogCore.Write((LogEventLevel)level, value, args);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(LogLevel level, string value, params ValueType[] args)
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