diff options
Diffstat (limited to 'lib/Utils/src')
-rw-r--r-- | lib/Utils/src/AdvancedTrace.cs | 52 | ||||
-rw-r--r-- | lib/Utils/src/Async/AsyncAccessSerializer.cs | 43 | ||||
-rw-r--r-- | lib/Utils/src/Async/AsyncQueue.cs | 12 | ||||
-rw-r--r-- | lib/Utils/src/Extensions/MemoryExtensions.cs | 48 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtil.cs | 45 | ||||
-rw-r--r-- | lib/Utils/src/Memory/UnsafeMemoryHandle.cs | 8 | ||||
-rw-r--r-- | lib/Utils/src/Native/SafeLibraryHandle.cs | 11 | ||||
-rw-r--r-- | lib/Utils/src/Resources/CallbackOpenHandle.cs | 44 | ||||
-rw-r--r-- | lib/Utils/src/Resources/ManagedLibrary.cs | 22 | ||||
-rw-r--r-- | lib/Utils/src/VNLib.Utils.csproj | 4 |
10 files changed, 153 insertions, 136 deletions
diff --git a/lib/Utils/src/AdvancedTrace.cs b/lib/Utils/src/AdvancedTrace.cs new file mode 100644 index 0000000..0507089 --- /dev/null +++ b/lib/Utils/src/AdvancedTrace.cs @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: AdvancedTrace.cs +* +* AdvancedTrace.cs is part of VNLib.Utils which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Utils is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* VNLib.Utils is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/. +*/ + +using System.Diagnostics; + +namespace VNLib.Utils +{ + /// <summary> + /// Provides methods for advanced tracing that are only optionally compiled + /// with the VNLIB_ADVANCED_TRACING symbol defined + /// </summary> + internal static class AdvancedTrace + { + const string AdvancedTraceSymbol = "VNLIB_ADVANCED_TRACING"; + + [Conditional(AdvancedTraceSymbol)] + public static void WriteLine(string? message) => Trace.WriteLine(message); + + [Conditional(AdvancedTraceSymbol)] + public static void WriteLine(string? message, string? category) => Trace.WriteLine(message, category); + + [Conditional(AdvancedTraceSymbol)] + public static void WriteLineIf(bool condition, string? message) => Trace.WriteLineIf(condition, message); + + [Conditional(AdvancedTraceSymbol)] + public static void WriteLineIf(bool condition, string? message, string? category) => Trace.WriteLineIf(condition, message, category); + + [Conditional(AdvancedTraceSymbol)] + public static void WriteLine(object? value) => Trace.WriteLine(value); + } +}
\ No newline at end of file diff --git a/lib/Utils/src/Async/AsyncAccessSerializer.cs b/lib/Utils/src/Async/AsyncAccessSerializer.cs index 76532bc..1beb4dc 100644 --- a/lib/Utils/src/Async/AsyncAccessSerializer.cs +++ b/lib/Utils/src/Async/AsyncAccessSerializer.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -37,42 +37,35 @@ namespace VNLib.Utils.Async /// Creates a base concrete implementation of an <see cref="IAsyncAccessSerializer{TMoniker}"/> /// </summary> /// <typeparam name="TMoniker">The moniker (key) type</typeparam> - public class AsyncAccessSerializer<TMoniker> : IAsyncAccessSerializer<TMoniker>, ICacheHolder where TMoniker : notnull + /// <remarks> + /// Initializes a new <see cref="AsyncAccessSerializer{TMoniker}"/> with the desired + /// caching pool size and initial capacity + /// </remarks> + /// <param name="maxPoolSize">The maxium number of cached wait entry objects</param> + /// <param name="initialCapacity">The initial capacity of the wait table</param> + /// <param name="keyComparer">The moniker key comparer</param> + public class AsyncAccessSerializer<TMoniker>(int maxPoolSize, int initialCapacity, IEqualityComparer<TMoniker>? keyComparer) + : IAsyncAccessSerializer<TMoniker>, ICacheHolder where TMoniker : notnull { /// <summary> /// The mutual exclusion monitor locking object /// </summary> - protected object StoreLock { get; } + protected object StoreLock { get; } = new(); /// <summary> /// A cache pool for <see cref="WaitEntry"/> /// </summary> - protected Stack<WaitEntry> EntryPool { get; } + protected Stack<WaitEntry> EntryPool { get; } = new(maxPoolSize); /// <summary> /// The table containing all active waiters /// </summary> - protected Dictionary<TMoniker, WaitEntry> WaitTable { get; } + protected Dictionary<TMoniker, WaitEntry> WaitTable { get; } = new(initialCapacity, keyComparer); /// <summary> /// The maxium number of elements allowed in the internal entry cache pool /// </summary> - protected int MaxPoolSize { get; } - - /// <summary> - /// Initializes a new <see cref="AsyncAccessSerializer{TMoniker}"/> with the desired - /// caching pool size and initial capacity - /// </summary> - /// <param name="maxPoolSize">The maxium number of cached wait entry objects</param> - /// <param name="initialCapacity">The initial capacity of the wait table</param> - /// <param name="keyComparer">The moniker key comparer</param> - public AsyncAccessSerializer(int maxPoolSize, int initialCapacity, IEqualityComparer<TMoniker>? keyComparer) - { - MaxPoolSize = maxPoolSize; - StoreLock = new(); - EntryPool = new(maxPoolSize); - WaitTable = new(initialCapacity, keyComparer); - } + protected int MaxPoolSize { get; } = maxPoolSize; ///<inheritdoc/> public virtual Task WaitAsync(TMoniker moniker, CancellationToken cancellation = default) @@ -464,11 +457,9 @@ namespace VNLib.Utils.Async * next task in the queue and be awaitable as a task */ - private sealed class TaskNode : Task + private sealed class TaskNode(Action<object?> callback, object item, CancellationToken cancellation) + : Task(callback, item, cancellation) { - public TaskNode(Action<object?> callback, object item, CancellationToken cancellation) : base(callback, item, cancellation) - { } - public TaskNode? Next { get; set; } } @@ -500,7 +491,7 @@ namespace VNLib.Utils.Async /// another waiter is not selected. /// </para> /// </summary> - /// <returns>A value that indicates if the task was transition successfully</returns> + /// <returns>A value that indicates if the task was transitioned successfully</returns> public readonly bool Release() { //return success if no next waiter diff --git a/lib/Utils/src/Async/AsyncQueue.cs b/lib/Utils/src/Async/AsyncQueue.cs index 45f1219..e94d08e 100644 --- a/lib/Utils/src/Async/AsyncQueue.cs +++ b/lib/Utils/src/Async/AsyncQueue.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -93,19 +93,13 @@ namespace VNLib.Utils.Async /// Initalizes a new unbound channel based queue /// </summary> /// <param name="ubOptions">Channel options</param> - public AsyncQueue(UnboundedChannelOptions ubOptions) - { - _channel = Channel.CreateUnbounded<T>(ubOptions); - } + public AsyncQueue(UnboundedChannelOptions ubOptions) => _channel = Channel.CreateUnbounded<T>(ubOptions); /// <summary> /// Initalizes a new bound channel based queue /// </summary> /// <param name="options">Channel options</param> - public AsyncQueue(BoundedChannelOptions options) - { - _channel = Channel.CreateBounded<T>(options); - } + public AsyncQueue(BoundedChannelOptions options) => _channel = Channel.CreateBounded<T>(options); /// <inheritdoc/> public bool TryEnque(T item) => _channel.Writer.TryWrite(item); diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs index 8f90525..e81580b 100644 --- a/lib/Utils/src/Extensions/MemoryExtensions.cs +++ b/lib/Utils/src/Extensions/MemoryExtensions.cs @@ -50,6 +50,8 @@ namespace VNLib.Utils.Extensions /// <returns>A new <see cref="OpenResourceHandle{T}"/> encapsulating the rented array</returns> public static UnsafeMemoryHandle<T> UnsafeAlloc<T>(this ArrayPool<T> pool, int size, bool zero = false) where T : unmanaged { + ArgumentNullException.ThrowIfNull(pool); + T[] array = pool.Rent(size); if (zero) @@ -766,52 +768,6 @@ namespace VNLib.Utils.Extensions #endregion /// <summary> - /// Slices the current array by the specified starting offset to the end - /// of the array - /// </summary> - /// <typeparam name="T">The array type</typeparam> - /// <param name="arr"></param> - /// <param name="start">The start offset of the new array slice</param> - /// <returns>The sliced array</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static T[] Slice<T>(this T[] arr, int start) - { - ArgumentNullException.ThrowIfNull(arr); - ArgumentOutOfRangeException.ThrowIfNegative(start); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(start, arr.Length); - - Range sliceRange = new(start, arr.Length - start); - return RuntimeHelpers.GetSubArray(arr, sliceRange); - } - - /// <summary> - /// Slices the current array by the specified starting offset to including the - /// speciifed number of items - /// </summary> - /// <typeparam name="T">The array type</typeparam> - /// <param name="arr"></param> - /// <param name="start">The start offset of the new array slice</param> - /// <param name="count">The size of the new array</param> - /// <returns>The sliced array</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static T[] Slice<T>(this T[] arr, int start, int count) - { - ArgumentNullException.ThrowIfNull(arr); - ArgumentOutOfRangeException.ThrowIfNegative(start); - ArgumentOutOfRangeException.ThrowIfNegative(count); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(start + count, arr.Length); - - if(count == 0) - { - return []; - } - - //Calc the slice range - Range sliceRange = new(start, start + count); - return RuntimeHelpers.GetSubArray(arr, sliceRange); - } - - /// <summary> /// Creates a new sub-sequence over the target handle. (allows for convient sub span) /// </summary> /// <typeparam name="T"></typeparam> diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs index b6eadbc..7ce7c81 100644 --- a/lib/Utils/src/Memory/MemoryUtil.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -1349,6 +1349,51 @@ namespace VNLib.Utils.Memory public static MemoryHandle GetMemoryHandleFromPointer(IntPtr value, GCHandle handle = default, IPinnable? pinnable = null) => new (value.ToPointer(), handle, pinnable); + + /// <summary> + /// Slices the current array by the specified starting offset to the end + /// of the array + /// </summary> + /// <typeparam name="T">The array type</typeparam> + /// <param name="arr"></param> + /// <param name="start">The start offset of the new array slice</param> + /// <returns>The sliced array</returns> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public static T[] SliceArray<T>(T[] arr, int start) + { + ArgumentNullException.ThrowIfNull(arr); + return SliceArray(arr, start, arr.Length - start); + } + + /// <summary> + /// Slices the current array by the specified starting offset to including the + /// speciifed number of items + /// </summary> + /// <typeparam name="T">The array type</typeparam> + /// <param name="arr"></param> + /// <param name="start">The start offset of the new array slice</param> + /// <param name="count">The size of the new array</param> + /// <returns>The sliced array</returns> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public static T[] SliceArray<T>(T[] arr, int start, int count) + { + ArgumentNullException.ThrowIfNull(arr); + ArgumentOutOfRangeException.ThrowIfNegative(start); + ArgumentOutOfRangeException.ThrowIfNegative(count); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(start + count, arr.Length); + + if (count == 0) + { + return []; + } + + //Calc the slice range + Range sliceRange = new(start, start + count); + return RuntimeHelpers.GetSubArray(arr, sliceRange); + } + /// <summary> /// Gets a <see cref="Span{T}"/> from the supplied address /// </summary> diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs index fbf96eb..1ed995f 100644 --- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs +++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs @@ -49,8 +49,9 @@ namespace VNLib.Utils.Memory PrivateHeap } - private readonly IntPtr _memoryPtr; private readonly int _length; + + private readonly IntPtr _memoryPtr; private readonly HandleType _handleType; private readonly T[]? _poolArr; @@ -115,6 +116,11 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] internal UnsafeMemoryHandle(IUnmangedHeap heap, IntPtr initial, int elements) { + //Never allow non-empty handles + Debug.Assert(heap != null); + Debug.Assert(initial != IntPtr.Zero); + Debug.Assert(elements > 0); + _pool = null; _poolArr = null; _heap = heap; diff --git a/lib/Utils/src/Native/SafeLibraryHandle.cs b/lib/Utils/src/Native/SafeLibraryHandle.cs index 5fb2283..4b4ead4 100644 --- a/lib/Utils/src/Native/SafeLibraryHandle.cs +++ b/lib/Utils/src/Native/SafeLibraryHandle.cs @@ -63,9 +63,8 @@ namespace VNLib.Utils.Native { //Get the method pointer IntPtr nativeMethod = NativeLibrary.GetExport(handle, functionName); - //Get the delegate for the function pointer - T method = Marshal.GetDelegateForFunctionPointer<T>(nativeMethod); - return new(this, method); + AdvancedTrace.WriteLine($"Loaded function '{functionName}' with address: 0x'{nativeMethod:x}'"); + return new(this, Marshal.GetDelegateForFunctionPointer<T>(nativeMethod)); } catch { @@ -90,6 +89,7 @@ namespace VNLib.Utils.Native this.ThrowIfClosed(); //Get the method pointer IntPtr nativeMethod = NativeLibrary.GetExport(handle, functionName); + AdvancedTrace.WriteLine($"Loaded function '{functionName}' with address: 0x'{nativeMethod:x}'"); //Get the delegate for the function pointer return Marshal.GetDelegateForFunctionPointer<T>(nativeMethod); } @@ -97,6 +97,7 @@ namespace VNLib.Utils.Native ///<inheritdoc/> protected override bool ReleaseHandle() { + AdvancedTrace.WriteLine($"Releasing library handle: 0x'{handle:x}'"); //Free the library and set the handle as invalid NativeLibrary.Free(handle); SetHandleAsInvalid(); @@ -211,7 +212,9 @@ namespace VNLib.Utils.Native NatveLibraryResolver resolver = new(libPath, assembly, searchPath); - return resolver.ResolveAndLoadLibrary(out library); + bool success = resolver.ResolveAndLoadLibrary(out library); + AdvancedTrace.WriteLineIf(success, $"Loaded library '{libPath}' with address: 0x'{library?.DangerousGetHandle():x}'"); + return success; } } } diff --git a/lib/Utils/src/Resources/CallbackOpenHandle.cs b/lib/Utils/src/Resources/CallbackOpenHandle.cs deleted file mode 100644 index 625bd45..0000000 --- a/lib/Utils/src/Resources/CallbackOpenHandle.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: CallbackOpenHandle.cs -* -* CallbackOpenHandle.cs is part of VNLib.Utils which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Utils is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published -* by the Free Software Foundation, either version 2 of the License, -* or (at your option) any later version. -* -* VNLib.Utils is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/. -*/ - -using System; - -namespace VNLib.Utils.Resources -{ - /// <summary> - /// A concrete <see cref="OpenHandle"/> for a defered operation or a resource that should be released or unwound - /// when the instance lifetime has ended. - /// </summary> - public sealed class CallbackOpenHandle : OpenHandle - { - private readonly Action ReleaseFunc; - /// <summary> - /// Creates a new generic <see cref="OpenHandle"/> with the specified release callback method - /// </summary> - /// <param name="release">The callback function to invoke when the <see cref="OpenHandle"/> is disposed</param> - public CallbackOpenHandle(Action release) => ReleaseFunc = release; - ///<inheritdoc/> - protected override void Free() => ReleaseFunc(); - } -}
\ No newline at end of file diff --git a/lib/Utils/src/Resources/ManagedLibrary.cs b/lib/Utils/src/Resources/ManagedLibrary.cs index 786a22d..c899156 100644 --- a/lib/Utils/src/Resources/ManagedLibrary.cs +++ b/lib/Utils/src/Resources/ManagedLibrary.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -25,7 +25,6 @@ using System; using System.IO; using System.Linq; -using System.Threading; using System.Reflection; using System.Runtime.Loader; using System.Collections.Generic; @@ -43,7 +42,7 @@ namespace VNLib.Utils.Resources { private readonly AssemblyLoadContext _loadContext; private readonly AssemblyDependencyResolver _resolver; - private readonly Lazy<Assembly> _lazyAssembly; + private readonly LazyInitializer<Assembly> _lazyAssembly; /// <summary> /// The absolute path to the assembly file @@ -53,7 +52,7 @@ namespace VNLib.Utils.Resources /// <summary> /// The assembly that is maintained by this loader /// </summary> - public Assembly Assembly => _lazyAssembly.Value; + public Assembly Assembly => _lazyAssembly.Instance; /// <summary> /// Initializes a new <see cref="ManagedLibrary"/> and skips @@ -74,11 +73,15 @@ namespace VNLib.Utils.Resources context.ResolvingUnmanagedDll += OnNativeLibraryResolving; //Lazy load the assembly - _lazyAssembly = new(LoadAssembly, LazyThreadSafetyMode.PublicationOnly); + _lazyAssembly = new(LoadAssembly); } //Load the assembly into the parent context - private Assembly LoadAssembly() => _loadContext.LoadFromAssemblyPath(AssemblyPath); + private Assembly LoadAssembly() + { + AdvancedTrace.WriteLine($"Loading managed library {AssemblyPath} into context {_loadContext.Name}"); + return _loadContext.LoadFromAssemblyPath(AssemblyPath); + } /// <summary> /// Raised when the load context that owns this assembly @@ -91,6 +94,7 @@ namespace VNLib.Utils.Resources /// </remarks> protected virtual void OnUnload(AssemblyLoadContext? ctx = null) { + AdvancedTrace.WriteLine($"Unloading managed library {AssemblyPath}"); //Remove resolving event handlers _loadContext.Unloading -= OnUnload; _loadContext.Resolving -= OnDependencyResolving; @@ -111,6 +115,8 @@ namespace VNLib.Utils.Resources //Resolve the desired asm dependency for the current context string? requestedDll = _resolver.ResolveUnmanagedDllToPath(libname); + AdvancedTrace.WriteLineIf(requestedDll != null,$"Resolving native library {libname} to path {requestedDll} for library {AssemblyPath}"); + //if the dep is resolved, seach in the assembly directory for the manageed dll only return requestedDll == null ? IntPtr.Zero : @@ -122,6 +128,8 @@ namespace VNLib.Utils.Resources //Resolve the desired asm dependency for the current context string? desiredAsm = _resolver.ResolveAssemblyToPath(asmName); + AdvancedTrace.WriteLineIf(desiredAsm != null, $"Resolving managed assembly {asmName.Name} to path {desiredAsm} for library {AssemblyPath}"); + //If the asm exists in the dir, load it return desiredAsm == null ? null : _loadContext.LoadFromAssemblyPath(desiredAsm); } @@ -137,6 +145,8 @@ namespace VNLib.Utils.Resources //See if the type is exported Type exp = TryGetExportedType<T>() ?? throw new EntryPointNotFoundException($"Imported assembly does not export desired type {typeof(T).FullName}"); + AdvancedTrace.WriteLine($"Creating instance of type {exp.FullName} from assembly {AssemblyPath}"); + //Create instance return (T)Activator.CreateInstance(exp)!; } diff --git a/lib/Utils/src/VNLib.Utils.csproj b/lib/Utils/src/VNLib.Utils.csproj index bda2164..7941a7b 100644 --- a/lib/Utils/src/VNLib.Utils.csproj +++ b/lib/Utils/src/VNLib.Utils.csproj @@ -26,6 +26,10 @@ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <DefineConstants>$(DefineConstants);VNLIB_ADVANCED_TRACING</DefineConstants> + </PropertyGroup> + <ItemGroup> <None Include="..\README.md"> <Pack>True</Pack> |