From 3ff90da4f02af47ea6d233fdd4445337ebe36452 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 30 Mar 2024 21:36:18 -0400 Subject: refactor: Updates, advanced tracing, http optimizations --- lib/Utils/src/AdvancedTrace.cs | 52 +++++++++++++++++++++++++++ lib/Utils/src/Async/AsyncAccessSerializer.cs | 43 +++++++++------------- lib/Utils/src/Async/AsyncQueue.cs | 12 ++----- lib/Utils/src/Extensions/MemoryExtensions.cs | 48 ++----------------------- lib/Utils/src/Memory/MemoryUtil.cs | 45 +++++++++++++++++++++++ lib/Utils/src/Memory/UnsafeMemoryHandle.cs | 8 ++++- lib/Utils/src/Native/SafeLibraryHandle.cs | 11 +++--- lib/Utils/src/Resources/CallbackOpenHandle.cs | 44 ----------------------- lib/Utils/src/Resources/ManagedLibrary.cs | 22 ++++++++---- lib/Utils/src/VNLib.Utils.csproj | 4 +++ 10 files changed, 153 insertions(+), 136 deletions(-) create mode 100644 lib/Utils/src/AdvancedTrace.cs delete mode 100644 lib/Utils/src/Resources/CallbackOpenHandle.cs (limited to 'lib/Utils') 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 +{ + /// + /// Provides methods for advanced tracing that are only optionally compiled + /// with the VNLIB_ADVANCED_TRACING symbol defined + /// + 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 /// /// The moniker (key) type - public class AsyncAccessSerializer : IAsyncAccessSerializer, ICacheHolder where TMoniker : notnull + /// + /// Initializes a new with the desired + /// caching pool size and initial capacity + /// + /// The maxium number of cached wait entry objects + /// The initial capacity of the wait table + /// The moniker key comparer + public class AsyncAccessSerializer(int maxPoolSize, int initialCapacity, IEqualityComparer? keyComparer) + : IAsyncAccessSerializer, ICacheHolder where TMoniker : notnull { /// /// The mutual exclusion monitor locking object /// - protected object StoreLock { get; } + protected object StoreLock { get; } = new(); /// /// A cache pool for /// - protected Stack EntryPool { get; } + protected Stack EntryPool { get; } = new(maxPoolSize); /// /// The table containing all active waiters /// - protected Dictionary WaitTable { get; } + protected Dictionary WaitTable { get; } = new(initialCapacity, keyComparer); /// /// The maxium number of elements allowed in the internal entry cache pool /// - protected int MaxPoolSize { get; } - - /// - /// Initializes a new with the desired - /// caching pool size and initial capacity - /// - /// The maxium number of cached wait entry objects - /// The initial capacity of the wait table - /// The moniker key comparer - public AsyncAccessSerializer(int maxPoolSize, int initialCapacity, IEqualityComparer? keyComparer) - { - MaxPoolSize = maxPoolSize; - StoreLock = new(); - EntryPool = new(maxPoolSize); - WaitTable = new(initialCapacity, keyComparer); - } + protected int MaxPoolSize { get; } = maxPoolSize; /// 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 callback, object item, CancellationToken cancellation) + : Task(callback, item, cancellation) { - public TaskNode(Action 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. /// /// - /// A value that indicates if the task was transition successfully + /// A value that indicates if the task was transitioned successfully 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 /// /// Channel options - public AsyncQueue(UnboundedChannelOptions ubOptions) - { - _channel = Channel.CreateUnbounded(ubOptions); - } + public AsyncQueue(UnboundedChannelOptions ubOptions) => _channel = Channel.CreateUnbounded(ubOptions); /// /// Initalizes a new bound channel based queue /// /// Channel options - public AsyncQueue(BoundedChannelOptions options) - { - _channel = Channel.CreateBounded(options); - } + public AsyncQueue(BoundedChannelOptions options) => _channel = Channel.CreateBounded(options); /// 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 /// A new encapsulating the rented array public static UnsafeMemoryHandle UnsafeAlloc(this ArrayPool pool, int size, bool zero = false) where T : unmanaged { + ArgumentNullException.ThrowIfNull(pool); + T[] array = pool.Rent(size); if (zero) @@ -765,52 +767,6 @@ namespace VNLib.Utils.Extensions #endregion - /// - /// Slices the current array by the specified starting offset to the end - /// of the array - /// - /// The array type - /// - /// The start offset of the new array slice - /// The sliced array - /// - public static T[] Slice(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); - } - - /// - /// Slices the current array by the specified starting offset to including the - /// speciifed number of items - /// - /// The array type - /// - /// The start offset of the new array slice - /// The size of the new array - /// The sliced array - /// - public static T[] Slice(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); - } - /// /// Creates a new sub-sequence over the target handle. (allows for convient sub span) /// 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); + + /// + /// Slices the current array by the specified starting offset to the end + /// of the array + /// + /// The array type + /// + /// The start offset of the new array slice + /// The sliced array + /// + /// + public static T[] SliceArray(T[] arr, int start) + { + ArgumentNullException.ThrowIfNull(arr); + return SliceArray(arr, start, arr.Length - start); + } + + /// + /// Slices the current array by the specified starting offset to including the + /// speciifed number of items + /// + /// The array type + /// + /// The start offset of the new array slice + /// The size of the new array + /// The sliced array + /// + /// + public static T[] SliceArray(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); + } + /// /// Gets a from the supplied address /// 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(nativeMethod); - return new(this, method); + AdvancedTrace.WriteLine($"Loaded function '{functionName}' with address: 0x'{nativeMethod:x}'"); + return new(this, Marshal.GetDelegateForFunctionPointer(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(nativeMethod); } @@ -97,6 +97,7 @@ namespace VNLib.Utils.Native /// 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 -{ - /// - /// A concrete for a defered operation or a resource that should be released or unwound - /// when the instance lifetime has ended. - /// - public sealed class CallbackOpenHandle : OpenHandle - { - private readonly Action ReleaseFunc; - /// - /// Creates a new generic with the specified release callback method - /// - /// The callback function to invoke when the is disposed - public CallbackOpenHandle(Action release) => ReleaseFunc = release; - /// - 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 _lazyAssembly; + private readonly LazyInitializer _lazyAssembly; /// /// The absolute path to the assembly file @@ -53,7 +52,7 @@ namespace VNLib.Utils.Resources /// /// The assembly that is maintained by this loader /// - public Assembly Assembly => _lazyAssembly.Value; + public Assembly Assembly => _lazyAssembly.Instance; /// /// Initializes a new 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); + } /// /// Raised when the load context that owns this assembly @@ -91,6 +94,7 @@ namespace VNLib.Utils.Resources /// 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() ?? 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 @@ True + + $(DefineConstants);VNLIB_ADVANCED_TRACING + + True -- cgit