aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Utils/src/Async/AsyncAccessSerializer.cs42
-rw-r--r--lib/Utils/src/InternalBufferTooSmallException.cs43
-rw-r--r--lib/Utils/src/Memory/Diagnostics/HeapDiagnosticException.cs4
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs7
-rw-r--r--lib/Utils/src/Memory/NativeHeap.cs15
-rw-r--r--lib/Utils/src/Memory/ProcessHeap.cs6
-rw-r--r--lib/Utils/src/Memory/SubSequence.cs5
-rw-r--r--lib/Utils/src/Memory/UnmanagedHeapBase.cs2
-rw-r--r--lib/Utils/src/Memory/VnString.cs36
-rw-r--r--lib/Utils/src/Memory/Win32PrivateHeap.cs8
-rw-r--r--lib/Utils/src/Native/NativeLibraryException.cs44
-rw-r--r--lib/Utils/src/Native/NativeMemoryException.cs44
-rw-r--r--lib/Utils/src/Native/NativeMemoryOutOfMemoryException.cs44
-rw-r--r--lib/Utils/src/NativeLibraryException.cs89
-rw-r--r--lib/Utils/src/Resources/ManagedLibrary.cs175
-rw-r--r--lib/Utils/src/Resources/OpenHandle.cs8
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
+ { }
+}