diff options
-rw-r--r-- | lib/Utils/src/Async/AsyncAccessSerializer.cs | 42 | ||||
-rw-r--r-- | lib/Utils/src/InternalBufferTooSmallException.cs | 43 | ||||
-rw-r--r-- | lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs | 4 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryHandle.cs | 7 | ||||
-rw-r--r-- | lib/Utils/src/Memory/NativeHeap.cs | 15 | ||||
-rw-r--r-- | lib/Utils/src/Memory/ProcessHeap.cs | 6 | ||||
-rw-r--r-- | lib/Utils/src/Memory/SubSequence.cs | 5 | ||||
-rw-r--r-- | lib/Utils/src/Memory/UnmanagedHeapBase.cs | 2 | ||||
-rw-r--r-- | lib/Utils/src/Memory/VnString.cs | 36 | ||||
-rw-r--r-- | lib/Utils/src/Memory/Win32PrivateHeap.cs | 8 | ||||
-rw-r--r-- | lib/Utils/src/Native/NativeLibraryException.cs | 44 | ||||
-rw-r--r-- | lib/Utils/src/Native/NativeMemoryException.cs | 44 | ||||
-rw-r--r-- | lib/Utils/src/Native/NativeMemoryOutOfMemoryException.cs | 44 | ||||
-rw-r--r-- | lib/Utils/src/NativeLibraryException.cs | 89 | ||||
-rw-r--r-- | lib/Utils/src/Resources/ManagedLibrary.cs | 175 | ||||
-rw-r--r-- | lib/Utils/src/Resources/OpenHandle.cs | 8 |
16 files changed, 432 insertions, 140 deletions
diff --git a/lib/Utils/src/Async/AsyncAccessSerializer.cs b/lib/Utils/src/Async/AsyncAccessSerializer.cs index 56887b1..0ee5a57 100644 --- a/lib/Utils/src/Async/AsyncAccessSerializer.cs +++ b/lib/Utils/src/Async/AsyncAccessSerializer.cs @@ -24,6 +24,7 @@ using System; using System.Threading; +using System.Diagnostics; using System.Threading.Tasks; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -69,14 +70,14 @@ namespace VNLib.Utils.Async { MaxPoolSize = maxPoolSize; StoreLock = new(); - EntryPool = new Stack<WaitEntry>(maxPoolSize); + EntryPool = new(maxPoolSize); WaitTable = new(initialCapacity, keyComparer); } ///<inheritdoc/> public virtual Task WaitAsync(TMoniker moniker, CancellationToken cancellation = default) { - //Token must not be cancelled + //Token must not be cancelled before entering wait cancellation.ThrowIfCancellationRequested(); WaitEnterToken token; @@ -93,7 +94,7 @@ namespace VNLib.Utils.Async } //Get waiter before leaving lock - token = wait.GetWaiter(); + wait.GetWaiter(out token); } return token.EnterWaitAsync(cancellation); @@ -163,7 +164,8 @@ namespace VNLib.Utils.Async } /// <summary> - /// Returns an empty <see cref="WaitEntry"/> back to the pool for reuse + /// Returns an empty <see cref="WaitEntry"/> back to the pool for reuse + /// (does not hold the store lock) /// </summary> /// <param name="entry">The entry to return to the pool</param> protected virtual void ReturnEntry(WaitEntry entry) @@ -214,40 +216,36 @@ namespace VNLib.Utils.Async protected class WaitEntry : VnDisposeable { private uint _waitCount; - private readonly SemaphoreSlim _waitHandle; + private readonly SemaphoreSlim _waitHandle = new(1, 1); /// <summary> /// A stored referrnece to the moniker while the wait exists /// </summary> - public TMoniker? Moniker { get; private set; } - - /// <summary> - /// Initializes a new <see cref="WaitEntry"/> - /// </summary> - public WaitEntry() - { - _waitHandle = new(1, 1); - Moniker = default!; - } + public TMoniker? Moniker { get; private set; } /// <summary> /// Gets a token used to enter the lock which may block, or yield async /// outside of a nested lock /// </summary> - /// <returns>The waiter used to enter a wait on the moniker</returns> - public WaitEnterToken GetWaiter() + /// <param name="enterToken">A referrence to the wait entry token</param> + /// <returns> + /// The incremented reference count. + /// </returns> + public uint GetWaiter(out WaitEnterToken enterToken) { /* * Increment wait count before entering the lock * A cancellation is the only way out, so cover that * during the async, only if the token is cancelable */ - _ = Interlocked.Increment(ref _waitCount); - return new(this); + + enterToken = new(this); + return Interlocked.Increment(ref _waitCount); } /// <summary> - /// Prepares a release + /// Prepares a release token and atomically decrements the waiter count + /// and returns the remaining number of waiters. /// </summary> /// <param name="releaser"> /// The token that should be used to release the exclusive lock held on @@ -270,7 +268,9 @@ namespace VNLib.Utils.Async public void Prepare(TMoniker? moniker) { Moniker = moniker; - _waitCount = 0; + + //Wait count should be 0 on calls to prepare, its a bug if not + Debug.Assert(_waitCount == 0); } /* diff --git a/lib/Utils/src/InternalBufferTooSmallException.cs b/lib/Utils/src/InternalBufferTooSmallException.cs new file mode 100644 index 0000000..a2d6375 --- /dev/null +++ b/lib/Utils/src/InternalBufferTooSmallException.cs @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: NativeLibraryException.cs +* +* NativeLibraryException.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 +{ + /// <summary> + /// Raised when an internal buffer was not propery sized for the opreation + /// </summary> + public class InternalBufferTooSmallException : OutOfMemoryException + { + public InternalBufferTooSmallException(string message) : base(message) + { } + + public InternalBufferTooSmallException(string message, Exception innerException) : base(message, innerException) + { } + + public InternalBufferTooSmallException() + { } + } +} diff --git a/lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs b/lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs index af76f6e..8410681 100644 --- a/lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs +++ b/lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -24,6 +24,8 @@ using System; +using VNLib.Utils.Native; + namespace VNLib.Utils.Memory.Diagnostics { /// <summary> diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs index bbf5a10..30d2b99 100644 --- a/lib/Utils/src/Memory/MemoryHandle.cs +++ b/lib/Utils/src/Memory/MemoryHandle.cs @@ -42,6 +42,7 @@ namespace VNLib.Utils.Memory /// </remarks> public sealed class MemoryHandle<T> : SafeHandleZeroOrMinusOneIsInvalid, IMemoryHandle<T>, IEquatable<MemoryHandle<T>> where T : unmanaged { + /// <summary> /// New <typeparamref name="T"/>* pointing to the base of the allocated block /// </summary> @@ -51,6 +52,7 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetOffset(0); } + /// <summary> /// New <see cref="IntPtr"/> pointing to the base of the allocated block /// </summary> @@ -60,6 +62,7 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (IntPtr)GetOffset(0); } + /// <summary> /// Gets a span over the entire allocated block /// </summary> @@ -112,7 +115,7 @@ namespace VNLib.Utils.Memory ZeroMemory = zero; //assign heap ref Heap = heap; - handle = initial; + SetHandle(initial); } /* @@ -238,7 +241,7 @@ namespace VNLib.Utils.Memory /// <exception cref="ObjectDisposedException"></exception> public bool Equals(MemoryHandle<T>? other) { - return other != null && IsClosed == other.IsClosed && _length == other._length && handle == other.handle; + return other != null && (IsClosed | other.IsClosed) == false && _length == other._length && handle == other.handle; } ///<inheritdoc/> diff --git a/lib/Utils/src/Memory/NativeHeap.cs b/lib/Utils/src/Memory/NativeHeap.cs index 33ab120..4b76866 100644 --- a/lib/Utils/src/Memory/NativeHeap.cs +++ b/lib/Utils/src/Memory/NativeHeap.cs @@ -23,6 +23,7 @@ */ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -63,7 +64,7 @@ namespace VNLib.Utils.Memory //Set defaults hFlags->Flags = flags; - hFlags->InternalFlags = creationFlags; + hFlags->CreationFlags = creationFlags; hFlags->HeapPointer = IntPtr.Zero; //Create the heap @@ -91,8 +92,10 @@ namespace VNLib.Utils.Memory Library = library }; + Trace.WriteLine($"Creating user defined native heap at {path}"); + //Get the create method - CreateHeapDelegate create = library.DangerousGetMethod<CreateHeapDelegate>(CREATE_METHOD_NAME); + CreateHeapDelegate create = library.DangerousGetMethod<CreateHeapDelegate>(CREATE_METHOD_NAME); //Create the new heap bool success = create(flags); @@ -102,6 +105,8 @@ namespace VNLib.Utils.Memory throw new NativeMemoryException("Failed to create the new heap, the heap create method returned a null pointer"); } + Trace.WriteLine($"Successfully created user defined native heap {flags->HeapPointer:x} with flags {flags->CreationFlags:x}"); + //Return the neap heap return new(flags, table); } @@ -115,7 +120,7 @@ namespace VNLib.Utils.Memory private HeapMethods MethodTable; - private unsafe NativeHeap(UnmanagedHeapDescriptor* flags, HeapMethods methodTable) :base(flags->InternalFlags, true) + private unsafe NativeHeap(UnmanagedHeapDescriptor* flags, HeapMethods methodTable) :base(flags->CreationFlags, true) { //Store heap pointer SetHandle(flags->HeapPointer); @@ -148,6 +153,8 @@ namespace VNLib.Utils.Memory //Cleanup the method table MethodTable = default; + Trace.WriteLine($"Successfully deestroyed user defined heap {handle:x}"); + return ret; } @@ -170,7 +177,7 @@ namespace VNLib.Utils.Memory { public IntPtr HeapPointer; - public HeapCreation InternalFlags; + public HeapCreation CreationFlags; public ERRNO Flags; } diff --git a/lib/Utils/src/Memory/ProcessHeap.cs b/lib/Utils/src/Memory/ProcessHeap.cs index 2792af9..84c9e0e 100644 --- a/lib/Utils/src/Memory/ProcessHeap.cs +++ b/lib/Utils/src/Memory/ProcessHeap.cs @@ -26,6 +26,8 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using VNLib.Utils.Native; + namespace VNLib.Utils.Memory { /// <summary> @@ -54,9 +56,7 @@ namespace VNLib.Utils.Memory /// </summary> public ProcessHeap() { -#if TRACE Trace.WriteLine($"Default heap instnace created {GetHashCode():x}"); -#endif } ///<inheritdoc/> @@ -82,9 +82,7 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> protected override void Free() { -#if TRACE Trace.WriteLine($"Default heap instnace disposed {GetHashCode():x}"); -#endif } ///<inheritdoc/> diff --git a/lib/Utils/src/Memory/SubSequence.cs b/lib/Utils/src/Memory/SubSequence.cs index 4e3c22c..def14ed 100644 --- a/lib/Utils/src/Memory/SubSequence.cs +++ b/lib/Utils/src/Memory/SubSequence.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -62,8 +62,7 @@ namespace VNLib.Utils.Memory //Check handle bounds MemoryUtil.CheckBounds(block, offset, (uint)size); - } - + } /// <summary> /// Gets a <see cref="Span{T}"/> that is offset from the base of the handle diff --git a/lib/Utils/src/Memory/UnmanagedHeapBase.cs b/lib/Utils/src/Memory/UnmanagedHeapBase.cs index eea84c8..bfa6736 100644 --- a/lib/Utils/src/Memory/UnmanagedHeapBase.cs +++ b/lib/Utils/src/Memory/UnmanagedHeapBase.cs @@ -27,6 +27,8 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; +using VNLib.Utils.Native; + using LPVOID = System.IntPtr; namespace VNLib.Utils.Memory diff --git a/lib/Utils/src/Memory/VnString.cs b/lib/Utils/src/Memory/VnString.cs index 7df6009..8542688 100644 --- a/lib/Utils/src/Memory/VnString.cs +++ b/lib/Utils/src/Memory/VnString.cs @@ -79,14 +79,20 @@ namespace VNLib.Utils.Memory } /// <summary> - /// Creates a new <see cref="VnString"/> around a <see cref="ReadOnlySpan{T}"/> or a <see cref="string"/> of data + /// Creates a new <see cref="VnString"/> around a <see cref="ReadOnlySpan{T}"/> or + /// a <see cref="string"/> of data /// </summary> /// <param name="data"><see cref="ReadOnlySpan{T}"/> of data to replicate</param> + /// <param name="heap">The heap to allocate the buffer from</param> /// <exception cref="OutOfMemoryException"></exception> - public VnString(ReadOnlySpan<char> data) + /// <remarks>Copies the value into internal memory</remarks> + public VnString(ReadOnlySpan<char> data, IUnmangedHeap? heap = null) { + //Default to shared heap + heap ??= MemoryUtil.Shared; + //Create new handle and copy incoming data to it - Handle = MemoryUtil.Shared.AllocAndCopy(data); + Handle = heap.AllocAndCopy(data); //Get subsequence over the whole copy of data _stringSequence = Handle.GetSubSequence(0, data.Length); @@ -98,14 +104,19 @@ namespace VNLib.Utils.Memory /// </summary> /// <param name="stream">Active stream of data to decode to a string</param> /// <param name="encoding"><see cref="Encoding"/> to use for decoding</param> + /// <param name="heap">The heap to allocate the buffer from</param> /// <param name="bufferSize">The size of the buffer to allocate during copying</param> /// <returns>The new <see cref="VnString"/> instance</returns> /// <exception cref="IOException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="OutOfMemoryException"></exception> /// <exception cref="InvalidOperationException"></exception> - public static VnString FromStream(Stream stream, Encoding encoding, uint bufferSize) + public static VnString FromStream(Stream stream, Encoding encoding, IUnmangedHeap heap, uint bufferSize) { + _ = stream ?? throw new ArgumentNullException(nameof(stream)); + _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); + _ = heap ?? throw new ArgumentNullException(nameof(heap)); + //Make sure the stream is readable if (!stream.CanRead) { @@ -117,7 +128,7 @@ namespace VNLib.Utils.Memory //Get the number of characters int numChars = encoding.GetCharCount(vnms.AsSpan()); //New handle - MemoryHandle<char> charBuffer = MemoryUtil.Shared.Alloc<char>(numChars); + MemoryHandle<char> charBuffer = heap.Alloc<char>(numChars); try { //Write characters to character buffer @@ -136,9 +147,9 @@ namespace VNLib.Utils.Memory else { //Create a new char bufer that will expand dyanmically - MemoryHandle<char> charBuffer = MemoryUtil.Shared.Alloc<char>(bufferSize); + MemoryHandle<char> charBuffer = heap.Alloc<char>(bufferSize); //Allocate a binary buffer - MemoryHandle<byte> binBuffer = MemoryUtil.Shared.Alloc<byte>(bufferSize); + MemoryHandle<byte> binBuffer = heap.Alloc<byte>(bufferSize); try { int length = 0; @@ -381,6 +392,15 @@ namespace VNLib.Utils.Memory /// <exception cref="ArgumentOutOfRangeException"></exception> public VnString Substring(int start) => Substring(start, (Length - start)); + /// <summary> + /// Creates a substring wrapper of the internal string designated by the + /// given range. + /// </summary> + /// <param name="range">The range of elements to create the wraper around</param> + /// <returns> + /// A new <see cref="VnString"/> instance pointing to the new substring window. + /// Memory belongs to the original string instance. + /// </returns> public VnString this[Range range] { get @@ -462,7 +482,7 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> public bool Equals(ReadOnlySpan<char> other, StringComparison stringComparison = StringComparison.Ordinal) => Length == other.Length && AsSpan().Equals(other, stringComparison); ///<inheritdoc/> - public bool Equals(SubSequence<char> other) => Length == other.Size && AsSpan().SequenceEqual(other.Span); + public bool Equals(in SubSequence<char> other) => Length == other.Size && AsSpan().SequenceEqual(other.Span); ///<inheritdoc/> public int CompareTo(string? other) => AsSpan().CompareTo(other, StringComparison.Ordinal); ///<inheritdoc/> diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs index d5f08fc..0a00a77 100644 --- a/lib/Utils/src/Memory/Win32PrivateHeap.cs +++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs @@ -28,6 +28,8 @@ using System.Runtime.Versioning; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using VNLib.Utils.Native; + using DWORD = System.Int64; using LPVOID = System.IntPtr; @@ -137,9 +139,8 @@ namespace VNLib.Utils.Memory { throw new NativeMemoryException("Heap could not be created"); } -#if TRACE Trace.WriteLine($"Win32 private heap {heapHandle:x} created"); -#endif + //Heap has been created so we can wrap it return new(heapHandle, cFlags, true); } @@ -202,9 +203,8 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> protected override bool ReleaseHandle() { -#if TRACE Trace.WriteLine($"Win32 private heap {handle:x} destroyed"); -#endif + return HeapDestroy(handle); } diff --git a/lib/Utils/src/Native/NativeLibraryException.cs b/lib/Utils/src/Native/NativeLibraryException.cs new file mode 100644 index 0000000..69cbf63 --- /dev/null +++ b/lib/Utils/src/Native/NativeLibraryException.cs @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: NativeLibraryException.cs +* +* NativeLibraryException.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.Native +{ + + /// <summary> + /// A base class for all native library related exceptions + /// </summary> + public class NativeLibraryException : SystemException + { + public NativeLibraryException(string message) : base(message) + { } + + public NativeLibraryException(string message, Exception innerException) : base(message, innerException) + { } + + public NativeLibraryException() + { } + } +} diff --git a/lib/Utils/src/Native/NativeMemoryException.cs b/lib/Utils/src/Native/NativeMemoryException.cs new file mode 100644 index 0000000..1385032 --- /dev/null +++ b/lib/Utils/src/Native/NativeMemoryException.cs @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: NativeMemoryException.cs +* +* NativeMemoryException.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.Native +{ + + /// <summary> + /// Base exception class for native memory related exceptions + /// </summary> + public class NativeMemoryException : NativeLibraryException + { + public NativeMemoryException(string message) : base(message) + { } + + public NativeMemoryException(string message, Exception innerException) : base(message, innerException) + { } + + public NativeMemoryException() + { } + } +} diff --git a/lib/Utils/src/Native/NativeMemoryOutOfMemoryException.cs b/lib/Utils/src/Native/NativeMemoryOutOfMemoryException.cs new file mode 100644 index 0000000..5a836da --- /dev/null +++ b/lib/Utils/src/Native/NativeMemoryOutOfMemoryException.cs @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: NativeMemoryOutOfMemoryException.cs +* +* NativeMemoryOutOfMemoryException.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.Native +{ + /// <summary> + /// Raised when a memory allocation or resize failed because there is + /// no more memory available + /// </summary> + public class NativeMemoryOutOfMemoryException : OutOfMemoryException + { + public NativeMemoryOutOfMemoryException(string message) : base(message) + { } + + public NativeMemoryOutOfMemoryException(string message, Exception innerException) : base(message, innerException) + { } + + public NativeMemoryOutOfMemoryException() + { } + } +} diff --git a/lib/Utils/src/NativeLibraryException.cs b/lib/Utils/src/NativeLibraryException.cs deleted file mode 100644 index 5c66852..0000000 --- a/lib/Utils/src/NativeLibraryException.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: NativeLibraryException.cs -* -* NativeLibraryException.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 -{ - /// <summary> - /// Raised when an internal buffer was not propery sized for the opreation - /// </summary> - public class InternalBufferTooSmallException : OutOfMemoryException - { - public InternalBufferTooSmallException(string message) : base(message) - {} - - public InternalBufferTooSmallException(string message, Exception innerException) : base(message, innerException) - {} - - public InternalBufferTooSmallException() - {} - } - - /// <summary> - /// A base class for all native library related exceptions - /// </summary> - public class NativeLibraryException : SystemException - { - public NativeLibraryException(string message) : base(message) - {} - - public NativeLibraryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeLibraryException() - {} - } - - /// <summary> - /// Base exception class for native memory related exceptions - /// </summary> - public class NativeMemoryException : NativeLibraryException - { - public NativeMemoryException(string message) : base(message) - {} - - public NativeMemoryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeMemoryException() - {} - } - - /// <summary> - /// Raised when a memory allocation or resize failed because there is - /// no more memory available - /// </summary> - public class NativeMemoryOutOfMemoryException : OutOfMemoryException - { - public NativeMemoryOutOfMemoryException(string message) : base(message) - {} - - public NativeMemoryOutOfMemoryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeMemoryOutOfMemoryException() - {} - } -} diff --git a/lib/Utils/src/Resources/ManagedLibrary.cs b/lib/Utils/src/Resources/ManagedLibrary.cs new file mode 100644 index 0000000..f9813a1 --- /dev/null +++ b/lib/Utils/src/Resources/ManagedLibrary.cs @@ -0,0 +1,175 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: ManagedLibrary.cs +* +* ManagedLibrary.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; +using System.IO; +using System.Linq; +using System.Threading; +using System.Reflection; +using System.Runtime.Loader; +using System.Runtime.InteropServices; + +using VNLib.Utils.IO; + +namespace VNLib.Utils.Resources +{ + /// <summary> + /// Allows dynamic/runtime loading of a managed assembly into the supplied <see cref="AssemblyLoadContext"/> + /// and provides a mechanism for resolving dependencies and native libraries. + /// </summary> + public class ManagedLibrary + { + private readonly AssemblyLoadContext _loadContext; + private readonly AssemblyDependencyResolver _resolver; + private readonly Lazy<Assembly> _lazyAssembly; + + /// <summary> + /// The absolute path to the assembly file + /// </summary> + public string AssemblyPath { get; } + + /// <summary> + /// The assembly that is maintained by this loader + /// </summary> + public Assembly Assembly => _lazyAssembly.Value; + + /// <summary> + /// Initializes a new <see cref="ManagedLibrary"/> and skips + /// initial file checks + /// </summary> + /// <param name="asmPath">The path to the assembly file and its dependencies</param> + /// <param name="context">The context to load the assembly into</param> + /// <exception cref="ArgumentNullException"></exception> + protected ManagedLibrary(string asmPath, AssemblyLoadContext context) + { + _loadContext = context ?? throw new ArgumentNullException(nameof(context)); + AssemblyPath = asmPath ?? throw new ArgumentNullException(nameof(asmPath)); + _resolver = new(asmPath); + + //Add resolver for context + context.Unloading += OnUnload; + context.Resolving += OnDependencyResolving; + context.ResolvingUnmanagedDll += OnNativeLibraryResolving; + + //Lazy load the assembly + _lazyAssembly = new(LoadAssembly, LazyThreadSafetyMode.PublicationOnly); + } + + private Assembly LoadAssembly() + { + //Load the assembly into the parent context + return _loadContext.LoadFromAssemblyPath(AssemblyPath); + } + + /// <summary> + /// Raised when the load context that owns this assembly + /// is unloaded. + /// </summary> + /// <param name="ctx">The context that is unloading</param> + /// <remarks> + /// This method should be called if the assembly is no longer + /// being used to free event handlers. + /// </remarks> + protected virtual void OnUnload(AssemblyLoadContext? ctx = null) + { + //Remove resolving event handlers + _loadContext.Unloading -= OnUnload; + _loadContext.Resolving -= OnDependencyResolving; + _loadContext.ResolvingUnmanagedDll -= OnNativeLibraryResolving; + } + + /* + * Resolves a native libary isolated to the requested assembly, which + * should be isolated to this assembly or one of its dependencies. + * + * We can usually assume the alc has the ability to fall back to safe + * directories (global ones also) to search for a platform native + * library, that is included with our assembly "package" + */ + + private IntPtr OnNativeLibraryResolving(Assembly assembly, string libname) + { + //Resolve the desired asm dependency for the current context + string? requestedDll = _resolver.ResolveUnmanagedDllToPath(libname); + + //if the dep is resolved, seach in the assembly directory for the manageed dll only + return requestedDll == null ? + IntPtr.Zero : + NativeLibrary.Load(requestedDll, assembly, DllImportSearchPath.AssemblyDirectory); + } + + private Assembly? OnDependencyResolving(AssemblyLoadContext context, AssemblyName asmName) + { + //Resolve the desired asm dependency for the current context + string? desiredAsm = _resolver.ResolveAssemblyToPath(asmName); + + //If the asm exists in the dir, load it + return desiredAsm == null ? null : _loadContext.LoadFromAssemblyPath(desiredAsm); + } + + /// <summary> + /// Loads the default assembly and gets the expected export type, + /// creates a new instance, and calls its parameterless constructor + /// </summary> + /// <returns>The desired type instance</returns> + /// <exception cref="EntryPointNotFoundException"></exception> + public T LoadTypeFromAssembly<T>() + { + Type resourceType = typeof(T); + + //See if the type is exported + Type exp = (from type in Assembly.GetExportedTypes() + where resourceType.IsAssignableFrom(type) + select type) + .FirstOrDefault() + ?? throw new EntryPointNotFoundException($"Imported assembly does not export desired type {resourceType.FullName}"); + + //Create instance + return (T)Activator.CreateInstance(exp)!; + } + + /// <summary> + /// Creates a new loader for the desired assembly. The assembly and its dependencies + /// will be loaded into the specified context. If no context is specified the current assemblie's load + /// context is captured. + /// </summary> + /// <param name="assemblyName">The name of the assmbly within the current plugin directory</param> + /// <param name="loadContext">The assembly load context to load the assmbly into</param> + /// <exception cref="FileNotFoundException"></exception> + public static ManagedLibrary LoadManagedAssembly(string assemblyName, AssemblyLoadContext loadContext) + { + _ = loadContext ?? throw new ArgumentNullException(nameof(loadContext)); + + //Make sure the file exists + if (!FileOperations.FileExists(assemblyName)) + { + throw new FileNotFoundException($"The desired assembly {assemblyName} could not be found at the file path"); + } + + //Init file info the get absolute path + FileInfo fi = new(assemblyName); + return new(fi.FullName, loadContext); + } + } +} diff --git a/lib/Utils/src/Resources/OpenHandle.cs b/lib/Utils/src/Resources/OpenHandle.cs index 6133a65..681e0cb 100644 --- a/lib/Utils/src/Resources/OpenHandle.cs +++ b/lib/Utils/src/Resources/OpenHandle.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -24,6 +24,7 @@ namespace VNLib.Utils.Resources { + /// <summary> /// Represents a base class for an open resource or operation that is valid while being held, /// and is released or unwound when disposed. @@ -33,6 +34,5 @@ namespace VNLib.Utils.Resources /// release actions are completed /// </remarks> public abstract class OpenHandle : VnDisposeable - { - } -}
\ No newline at end of file + { } +} |