aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils/src')
-rw-r--r--lib/Utils/src/ArgumentList.cs9
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs37
-rw-r--r--lib/Utils/src/Native/NatveLibraryResolver.cs299
-rw-r--r--lib/Utils/src/Native/SafeLibraryHandle.cs260
-rw-r--r--lib/Utils/src/Resources/LazyInitializer.cs112
5 files changed, 544 insertions, 173 deletions
diff --git a/lib/Utils/src/ArgumentList.cs b/lib/Utils/src/ArgumentList.cs
index 235e62c..c02ebee 100644
--- a/lib/Utils/src/ArgumentList.cs
+++ b/lib/Utils/src/ArgumentList.cs
@@ -24,6 +24,7 @@
using System;
using System.Linq;
+using System.Collections;
using System.Collections.Generic;
namespace VNLib.Utils
@@ -31,7 +32,7 @@ namespace VNLib.Utils
/// <summary>
/// Provides methods for parsing an argument list
/// </summary>
- public class ArgumentList : IIndexable<int, string>
+ public class ArgumentList : IIndexable<int, string>, IEnumerable<string>
{
private readonly List<string> _args;
@@ -96,6 +97,10 @@ namespace VNLib.Utils
return index == -1 || index + 1 >= _args.Count ? null : this[index + 1];
}
-
+ ///<inheritdoc/>
+ public IEnumerator<string> GetEnumerator() => _args.GetEnumerator();
+
+ ///<inheritdoc/>
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 6efd5ba..b6eadbc 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -25,7 +25,6 @@
using System;
using System.Buffers;
using System.Security;
-using System.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
@@ -33,6 +32,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86;
using VNLib.Utils.Memory.Diagnostics;
+using VNLib.Utils.Resources;
namespace VNLib.Utils.Memory
{
@@ -102,13 +102,13 @@ namespace VNLib.Utils.Memory
/// The backing heap
/// is determined by the OS type and process environment varibles.
/// </remarks>
- public static IUnmangedHeap Shared => _sharedHeap.Value;
+ public static IUnmangedHeap Shared => _lazyHeap.Instance;
-
- private static readonly Lazy<IUnmangedHeap> _sharedHeap = InitHeapInternal();
+
+ private static readonly LazyInitializer<IUnmangedHeap> _lazyHeap = InitHeapInternal();
//Avoiding static initializer
- private static Lazy<IUnmangedHeap> InitHeapInternal()
+ private static LazyInitializer<IUnmangedHeap> InitHeapInternal()
{
//Get env for heap diag
_ = ERRNO.TryParse(Environment.GetEnvironmentVariable(SHARED_HEAP_ENABLE_DIAGNOISTICS_ENV), out ERRNO diagEnable);
@@ -116,22 +116,17 @@ namespace VNLib.Utils.Memory
Trace.WriteLineIf(diagEnable, "Shared heap diagnostics enabled");
Trace.WriteLineIf(globalZero, "Shared heap global zero enabled");
-
- Lazy<IUnmangedHeap> heap = new (() => InitHeapInternal(true, diagEnable, globalZero), LazyThreadSafetyMode.PublicationOnly);
-
- //Cleanup the heap on process exit
- AppDomain.CurrentDomain.DomainUnload += DomainUnloaded;
-
- return heap;
- }
- private static void DomainUnloaded(object? sender, EventArgs e)
- {
- //Dispose the heap if allocated
- if (_sharedHeap.IsValueCreated)
+ return new(() =>
{
- _sharedHeap.Value.Dispose();
- }
+ //Init shared heap instance
+ IUnmangedHeap heap = InitHeapInternal(true, diagEnable, globalZero);
+
+ //Register domain unload event
+ AppDomain.CurrentDomain.DomainUnload += (_, _) => heap.Dispose();
+
+ return heap;
+ });
}
/// <summary>
@@ -147,7 +142,7 @@ namespace VNLib.Utils.Memory
* If heap is allocated and the heap type is a tracked heap,
* get the heap's stats, otherwise return an empty handle
*/
- return _sharedHeap.IsValueCreated && _sharedHeap.Value is TrackedHeapWrapper h
+ return _lazyHeap.IsLoaded && _lazyHeap.Instance is TrackedHeapWrapper h
? h.GetCurrentStats() : default;
}
@@ -1610,4 +1605,4 @@ namespace VNLib.Utils.Memory
public readonly void Validate(nuint count) => CheckBounds(handle, offset, count);
}
}
-} \ No newline at end of file
+}
diff --git a/lib/Utils/src/Native/NatveLibraryResolver.cs b/lib/Utils/src/Native/NatveLibraryResolver.cs
new file mode 100644
index 0000000..b888f42
--- /dev/null
+++ b/lib/Utils/src/Native/NatveLibraryResolver.cs
@@ -0,0 +1,299 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: NatveLibraryResolver.cs
+*
+* NatveLibraryResolver.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.Reflection;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Diagnostics.CodeAnalysis;
+
+namespace VNLib.Utils.Native
+{
+ /// <summary>
+ /// Uses a supplied library state to resolve and load a platform native library
+ /// into the process memory space.
+ /// </summary>
+ /// <param name="suppliedLibraryPath">The raw caller-supplied library path to resolve</param>
+ /// <param name="assembly">A assembly loading the desired library</param>
+ /// <param name="searchPath">The dll loader search path requirements</param>
+ internal readonly struct NatveLibraryResolver(string suppliedLibraryPath, Assembly assembly, DllImportSearchPath searchPath)
+ {
+ private readonly string _libFileName = Path.GetFileName(suppliedLibraryPath);
+ private readonly string? _relativeDir = Path.GetDirectoryName(suppliedLibraryPath);
+
+ internal readonly bool HasRelativeDir => _relativeDir != null;
+
+ internal readonly bool IsFullPath => Path.IsPathRooted(suppliedLibraryPath);
+
+ /// <summary>
+ /// Resolves and attempts to load the current library into the current process.
+ /// </summary>
+ /// <param name="library">The <see cref="SafeLibraryHandle"/> if the library was successfully resolved</param>
+ /// <returns>True if the library was resolved and loaded into the process, false otherwise</returns>
+ internal readonly bool ResolveAndLoadLibrary([NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ //Try naive load if the path is an absolute path
+ if (IsFullPath && _tryLoad(suppliedLibraryPath, out library))
+ {
+ return true;
+ }
+
+ //Try to load the library from the relative directory
+ if (HasRelativeDir && TryLoadRelativeToWorkingDir(out library))
+ {
+ return true;
+ }
+
+ //Path is just a file name, so search for prefixes and extensions loading directly
+ if (TryNaiveLoadLibrary(out library))
+ {
+ return true;
+ }
+
+ //Try searching for the file in directories
+ return TryLoadFromSafeDirs(out library);
+ }
+
+ /*
+ * Naive load builds file names that are platform dependent
+ * and probes for the library using the NativeLibrary.TryLoad rules
+ * to load the library.
+ *
+ * If the file has an extension, it will attempt to load the library
+ * directly using the file name. Otherwise, it will continue to probe
+ */
+ private readonly bool TryNaiveLoadLibrary([NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ foreach (string probingFileName in GetProbingFileNames(_libFileName))
+ {
+ if (_tryLoad(probingFileName, out library))
+ {
+ return true;
+ }
+ }
+
+ library = null;
+ return false;
+ }
+
+ /*
+ * Attempts to probe library files names that are located inside safe directories
+ * specified by the caller using the DllImportSearchPath enum.
+ */
+ private readonly bool TryLoadFromSafeDirs([NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ //Try enumerating safe directories
+ if (searchPath.HasFlag(DllImportSearchPath.SafeDirectories))
+ {
+ foreach (string dir in GetSpecialDirPaths())
+ {
+ if (TryLoadInDirectory(dir, out library))
+ {
+ return true;
+ }
+ }
+ }
+
+ //Check application directory first (including subdirectories)
+ if (searchPath.HasFlag(DllImportSearchPath.ApplicationDirectory))
+ {
+ //get the current directory
+ if (TryLoadInDirectory(Directory.GetCurrentDirectory(), out library))
+ {
+ return true;
+ }
+ }
+
+ //See if search in the calling assembly directory
+ if (searchPath.HasFlag(DllImportSearchPath.AssemblyDirectory))
+ {
+ //Get the calling assmblies directory
+ string libDir = Assembly.GetCallingAssembly().Location;
+ Debug.WriteLine("Native library searching for calling assembly location:{0} ", libDir);
+ if (TryLoadInDirectory(libDir, out library))
+ {
+ return true;
+ }
+ }
+
+ //Search system32 dir
+ if (searchPath.HasFlag(DllImportSearchPath.System32))
+ {
+ string sys32Dir = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86);
+
+ //Get the system directory
+ if (TryLoadInDirectory(sys32Dir, out library))
+ {
+ return true;
+ }
+ }
+
+ library = null;
+ return false;
+ }
+
+ /*
+ * Users may specify realtive directories to search for the library
+ * in the current working directory, so this function attempts to load
+ * the library from the relative directory if the user has specified one
+ */
+ private readonly bool TryLoadRelativeToWorkingDir([NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ string libDir = Directory.GetCurrentDirectory();
+ return TryLoadInDirectory(Path.Combine(libDir, _relativeDir!), out library);
+ }
+
+ /*
+ * Attempts to load libraries that are located in the specified directory
+ * by probing for the library file name in the directory path using
+ * prefixes and extensions
+ */
+ private readonly bool TryLoadInDirectory(string baseDir, [NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ IEnumerable<string> fullProbingPaths = GetProbingFileNames(_libFileName).Select(p => Path.Combine(baseDir, p));
+
+ foreach (string probingFilePath in fullProbingPaths)
+ {
+ if (_tryLoad(probingFilePath, out library))
+ {
+ return true;
+ }
+ }
+
+ library = null;
+ return false;
+ }
+
+ /*
+ * core load function
+ */
+ internal readonly bool _tryLoad(string filePath, [NotNullWhen(true)] out SafeLibraryHandle? library)
+ {
+ //Attempt a naive load
+ if (NativeLibrary.TryLoad(filePath, assembly, searchPath, out IntPtr libHandle))
+ {
+ library = SafeLibraryHandle.FromExisting(libHandle, true);
+ return true;
+ }
+
+ library = null;
+ return false;
+ }
+
+ private static readonly Environment.SpecialFolder[] SafeDirs =
+ [
+ Environment.SpecialFolder.SystemX86,
+ Environment.SpecialFolder.System,
+ Environment.SpecialFolder.Windows,
+ Environment.SpecialFolder.ProgramFilesX86,
+ Environment.SpecialFolder.ProgramFiles
+ ];
+
+ private static IEnumerable<string> GetSpecialDirPaths() => SafeDirs.Select(Environment.GetFolderPath);
+
+ private static IEnumerable<string> GetProbingFileNames(string libraryFileName)
+ {
+ //Inlcude the library file name if it has an extension
+ if (Path.HasExtension(libraryFileName))
+ {
+ yield return libraryFileName;
+ }
+
+ foreach (string prefix in GetNativeLibPrefixs())
+ {
+ foreach (string libExtension in GetNativeLibExtension())
+ {
+ yield return GetLibraryFileName(prefix, libraryFileName, libExtension);
+ }
+ }
+
+ static string GetLibraryFileName(string prefix, string libPath, string extension)
+ {
+ //Get dir name from the lib path if it has one
+
+
+ libPath = Path.Combine(prefix, libPath);
+
+ //If the library path already has an extension, just search for the file
+ if (!Path.HasExtension(libPath))
+ {
+ //slice the lib to its file name
+ libPath = Path.GetFileName(libPath);
+
+ if (extension.Length > 0)
+ {
+ libPath = Path.ChangeExtension(libPath, extension);
+ }
+ }
+
+ return libPath;
+ }
+
+ static string[] GetNativeLibPrefixs()
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ return [""];
+ }
+ else if (OperatingSystem.IsMacOS())
+ {
+ return ["", "lib"];
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ return ["", "lib_", "lib"];
+ }
+ else
+ {
+ Debug.Fail("Unknown OS type");
+ return [];
+ }
+ }
+
+ static string[] GetNativeLibExtension()
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ return ["", ".dll"];
+ }
+ else if (OperatingSystem.IsMacOS())
+ {
+ return ["", ".dylib"];
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ return ["", ".so", ".so.1"];
+ }
+ else
+ {
+ Debug.Fail("Unknown OS type");
+ return [];
+ }
+ }
+ }
+ }
+}
diff --git a/lib/Utils/src/Native/SafeLibraryHandle.cs b/lib/Utils/src/Native/SafeLibraryHandle.cs
index ed9dd16..5fb2283 100644
--- a/lib/Utils/src/Native/SafeLibraryHandle.cs
+++ b/lib/Utils/src/Native/SafeLibraryHandle.cs
@@ -23,10 +23,7 @@
*/
using System;
-using System.IO;
-using System.Linq;
using System.Reflection;
-using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
@@ -42,128 +39,7 @@ namespace VNLib.Utils.Native
///<inheritdoc/>
public override bool IsInvalid => handle == IntPtr.Zero;
- private SafeLibraryHandle(IntPtr libHandle) : base(IntPtr.Zero, true)
- {
- //Init handle
- SetHandle(libHandle);
- }
-
- /// <summary>
- /// Finds and loads the specified native libary into the current process by its name at runtime
- /// </summary>
- /// <param name="libPath">The path (or name of libary) to search for</param>
- /// <param name="searchPath">
- /// The <see cref="DllImportSearchPath"/> used to search for libaries
- /// within the current filesystem
- /// </param>
- /// <returns>The loaded <see cref="SafeLibraryHandle"/></returns>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="DllNotFoundException"></exception>
- public static SafeLibraryHandle LoadLibrary(string libPath, DllImportSearchPath searchPath = DllImportSearchPath.ApplicationDirectory)
- {
- ArgumentNullException.ThrowIfNull(libPath);
- //See if the path includes a file extension
- return TryLoadLibrary(libPath, searchPath, out SafeLibraryHandle? lib)
- ? lib
- : throw new DllNotFoundException($"The library {libPath} or one of its dependencies could not be found");
- }
-
- /// <summary>
- /// Attempts to load the specified native libary into the current process by its name at runtime
- /// </summary>
- ///<param name="libPath">The path (or name of libary) to search for</param>
- /// <param name="searchPath">
- /// The <see cref="DllImportSearchPath"/> used to search for libaries
- /// within the current filesystem
- /// </param>
- /// <param name="lib">The handle to the libary if successfully loaded</param>
- /// <returns>True if the libary was found and loaded into the current process</returns>
- public static bool TryLoadLibrary(string libPath, DllImportSearchPath searchPath, [NotNullWhen(true)] out SafeLibraryHandle? lib)
- {
- lib = null;
- //Allow full rooted paths
- if (Path.IsPathRooted(libPath))
- {
- //Attempt a native load
- if (NativeLibrary.TryLoad(libPath, out IntPtr libHandle))
- {
- lib = new(libHandle);
- return true;
- }
- return false;
- }
- //Check application directory first (including subdirectories)
- if ((searchPath & DllImportSearchPath.ApplicationDirectory) > 0)
- {
- //get the current directory
- string libDir = Directory.GetCurrentDirectory();
- if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib))
- {
- return true;
- }
- }
- //See if search in the calling assembly directory
- if ((searchPath & DllImportSearchPath.AssemblyDirectory) > 0)
- {
- //Get the calling assmblies directory
- string libDir = Assembly.GetCallingAssembly().Location;
- Debug.WriteLine("Native library searching for calling assembly location:{0} ", libDir);
- if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib))
- {
- return true;
- }
- }
- //Search system32 dir
- if ((searchPath & DllImportSearchPath.System32) > 0)
- {
- //Get the system directory
- string libDir = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86);
- if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib))
- {
- return true;
- }
- }
- //Attempt a native load
- {
- if (NativeLibrary.TryLoad(libPath, out IntPtr libHandle))
- {
- lib = new(libHandle);
- return true;
- }
- return false;
- }
- }
-
- private static bool TryLoadLibraryInternal(string libDir, string libPath, SearchOption dirSearchOptions, [NotNullWhen(true)] out SafeLibraryHandle? libary)
- {
- //Try to find the libary file
- string? libFile = GetLibraryFile(libDir, libPath, dirSearchOptions);
- //Load libary
- if (libFile != null && NativeLibrary.TryLoad(libFile, out IntPtr libHandle))
- {
- libary = new SafeLibraryHandle(libHandle);
- return true;
- }
- libary = null;
- return false;
- }
-
- private static string? GetLibraryFile(string dirPath, string libPath, SearchOption search)
- {
- //If the library path already has an extension, just search for the file
- if (Path.HasExtension(libPath))
- {
- return Directory.EnumerateFiles(dirPath, libPath, search).FirstOrDefault();
- }
- else
- {
- //slice the lib to its file name
- libPath = Path.GetFileName(libPath);
- libPath = Path.ChangeExtension(libPath, OperatingSystem.IsWindows() ? ".dll" : ".so");
- //Select the first file that matches the name
- return Directory.EnumerateFiles(dirPath, libPath, search).FirstOrDefault();
- }
- }
+ private SafeLibraryHandle(IntPtr libHandle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) => SetHandle(libHandle);
/// <summary>
/// Loads a native function pointer from the library of the specified name and
@@ -179,7 +55,7 @@ namespace VNLib.Utils.Native
{
//Increment handle count before obtaining a method
bool success = false;
- DangerousAddRef(ref success);
+ DangerousAddRef(ref success);
ObjectDisposedException.ThrowIf(success == false, this);
@@ -218,40 +94,124 @@ namespace VNLib.Utils.Native
return Marshal.GetDelegateForFunctionPointer<T>(nativeMethod);
}
+ ///<inheritdoc/>
+ protected override bool ReleaseHandle()
+ {
+ //Free the library and set the handle as invalid
+ NativeLibrary.Free(handle);
+ SetHandleAsInvalid();
+ return true;
+ }
+
/// <summary>
- /// Loads a native method from the library of the specified name and managed delegate
+ /// Finds and loads the specified native libary into the current process by its name at runtime.
+ /// This function defaults to the executing assembly
/// </summary>
- /// <typeparam name="T">The native method delegate type</typeparam>
- /// <param name="methodName">The name of the native method</param>
- /// <returns>A wapper handle around the native method delegate</returns>
+ /// <param name="libPath">The path (or name of libary) to search for</param>
+ /// <param name="searchPath">
+ /// The <see cref="DllImportSearchPath"/> used to search for libaries
+ /// within the current filesystem
+ /// </param>
+ /// <returns>The loaded <see cref="SafeLibraryHandle"/></returns>
/// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="ObjectDisposedException">If the handle is closed or invalid</exception>
- /// <exception cref="EntryPointNotFoundException">When the specified entrypoint could not be found</exception>
- [Obsolete("Updated naming, use GetFunction<T>() instead")]
- public SafeMethodHandle<T> GetMethod<T>(string methodName) where T : Delegate => GetFunction<T>(methodName);
+ /// <exception cref="DllNotFoundException"></exception>
+ public static SafeLibraryHandle LoadLibrary(string libPath, DllImportSearchPath searchPath = DllImportSearchPath.ApplicationDirectory)
+ {
+ //See if the path includes a file extension
+ return TryLoadLibrary(libPath, searchPath, out SafeLibraryHandle? lib)
+ ? lib
+ : throw new DllNotFoundException($"The library '{libPath}' or one of its dependencies could not be found");
+ }
/// <summary>
- /// Gets an delegate wrapper for the specified method without tracking its referrence.
- /// The caller must manage the <see cref="SafeLibraryHandle"/> referrence count in order
- /// to not leak resources or cause process corruption
+ /// Finds and loads the specified native libary into the current process by its name at runtime
/// </summary>
- /// <typeparam name="T">The native method delegate type</typeparam>
- /// <param name="methodName">The name of the native method</param>
- /// <returns>A the delegate wrapper on the native method</returns>
+ /// <param name="libPath">The path (or name of libary) to search for</param>
+ /// <param name="searchPath">
+ /// The <see cref="DllImportSearchPath"/> used to search for libaries
+ /// within the current filesystem
+ /// </param>
+ /// <param name="assembly">The assembly loading the native library</param>
+ /// <returns>The loaded <see cref="SafeLibraryHandle"/></returns>
/// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="ObjectDisposedException">If the handle is closed or invalid</exception>
- /// <exception cref="EntryPointNotFoundException">When the specified entrypoint could not be found</exception>
- [Obsolete("Updated naming, use DangerousGetFunction<T>() instead")]
- public T DangerousGetMethod<T>(string methodName) where T : Delegate => DangerousGetFunction<T>(methodName);
+ /// <exception cref="DllNotFoundException"></exception>
+ public static SafeLibraryHandle LoadLibrary(
+ string libPath,
+ Assembly assembly,
+ DllImportSearchPath searchPath = DllImportSearchPath.ApplicationDirectory
+ )
+ {
+ //See if the path includes a file extension
+ return TryLoadLibrary(libPath, assembly, searchPath, out SafeLibraryHandle? lib)
+ ? lib
+ : throw new DllNotFoundException($"The library '{libPath}' or one of its dependencies could not be found");
+ }
+ /// <summary>
+ /// Creates a new <see cref="SafeLibraryHandle"/> from an existing library pointer.
+ /// </summary>
+ /// <param name="libHandle">A pointer to the existing (and loaded) library</param>
+ /// <param name="ownsHandle">A value that specifies whether the wrapper owns the library handle now</param>
+ /// <returns>A safe library wrapper around the existing library pointer</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public unsafe static SafeLibraryHandle FromExisting(nint libHandle, bool ownsHandle)
+ {
+ ArgumentNullException.ThrowIfNull(libHandle.ToPointer(), nameof(libHandle));
+ return new(libHandle, ownsHandle);
+ }
- ///<inheritdoc/>
- protected override bool ReleaseHandle()
+ /// <summary>
+ /// Attempts to load the specified native libary into the current process by its name at runtime.
+ /// This function defaults to the executing assembly
+ /// </summary>
+ ///<param name="libPath">The path (or name of libary) to search for</param>
+ /// <param name="searchPath">
+ /// The <see cref="DllImportSearchPath"/> used to search for libaries
+ /// within the current filesystem
+ /// </param>
+ /// <param name="library">The handle to the libary if successfully loaded</param>
+ /// <returns>True if the libary was found and loaded into the current process</returns>
+ public static bool TryLoadLibrary(
+ string libPath,
+ DllImportSearchPath searchPath,
+ [NotNullWhen(true)] out SafeLibraryHandle? library
+ )
{
- //Free the library and set the handle as invalid
- NativeLibrary.Free(handle);
- SetHandleAsInvalid();
- return true;
+ return TryLoadLibrary(
+ libPath,
+ Assembly.GetExecutingAssembly(), //Use the executing assembly as the default loading assembly
+ searchPath,
+ out library
+ );
+ }
+
+
+
+ /// <summary>
+ /// Attempts to load the specified native libary into the current process by its name at runtime
+ /// </summary>
+ ///<param name="libPath">The path (or name of libary) to search for</param>
+ /// <param name="searchPath">
+ /// The <see cref="DllImportSearchPath"/> used to search for libaries
+ /// within the current filesystem
+ /// </param>
+ /// <param name="library">The handle to the libary if successfully loaded</param>
+ /// <param name="assembly">The assembly loading the native library</param>
+ /// <returns>True if the libary was found and loaded into the current process</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static bool TryLoadLibrary(
+ string libPath,
+ Assembly assembly,
+ DllImportSearchPath searchPath,
+ [NotNullWhen(true)] out SafeLibraryHandle? library
+ )
+ {
+ ArgumentNullException.ThrowIfNull(libPath);
+ ArgumentNullException.ThrowIfNull(assembly);
+
+ NatveLibraryResolver resolver = new(libPath, assembly, searchPath);
+
+ return resolver.ResolveAndLoadLibrary(out library);
}
}
}
diff --git a/lib/Utils/src/Resources/LazyInitializer.cs b/lib/Utils/src/Resources/LazyInitializer.cs
new file mode 100644
index 0000000..5de43e0
--- /dev/null
+++ b/lib/Utils/src/Resources/LazyInitializer.cs
@@ -0,0 +1,112 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: LazyInitializer.cs
+*
+* LazyInitializer.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.Threading;
+using System.Diagnostics;
+
+namespace VNLib.Utils.Resources
+{
+ /// <summary>
+ /// A lazy initializer that creates a single instance of a type
+ /// and shares it across all threads. This class simply guarantees
+ /// that the instance is only created once and shared across all
+ /// threads as efficiently as possible for long running processes.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="initalizer">The callback function that initializes the instance</param>
+ public sealed class LazyInitializer<T>(Func<T> initalizer)
+ {
+ private readonly object _lock = new();
+ private readonly Func<T> initalizer = initalizer ?? throw new ArgumentNullException(nameof(initalizer));
+
+ private T? _instance;
+ private bool _isLoaded;
+
+ /// <summary>
+ /// A value indicating if the instance has ben loaded
+ /// </summary>
+ public bool IsLoaded => _isLoaded;
+
+ /// <summary>
+ /// Gets or creates the instance only once and returns
+ /// the shared instance
+ /// </summary>
+ /// <remarks>
+ /// NOTE:
+ /// Accessing this property may block the calling thread
+ /// if the instance has not yet been loaded. Only one thread
+ /// will create the instance, all other threads will wait
+ /// for the instance to be created.
+ /// </remarks>
+ public T Instance
+ {
+ get
+ {
+ //See if instance is already loaded (this read is atomic in .NET)
+ if (_isLoaded)
+ {
+ return _instance!;
+ }
+
+ /*
+ * Instance has not yet been loaded. Only one thread
+ * must load the object, all other threads must wait
+ * for the object to be loaded.
+ */
+
+ if (Monitor.TryEnter(_lock, 0))
+ {
+ try
+ {
+ /*
+ * Lock was entered without waiting (lock was available), this will now be
+ * the thread that invokes the load function
+ */
+
+ _instance = initalizer();
+
+ //Finally set the load state
+ _isLoaded = true;
+ }
+ finally
+ {
+ Monitor.Exit(_lock);
+ }
+ }
+ else
+ {
+ //wait for lock to be released, when it is, the object should be loaded
+ Monitor.Enter(_lock);
+ Monitor.Exit(_lock);
+
+ //object instance should now be available to non-creating threads
+ Debug.Assert(_isLoaded);
+ }
+
+ return _instance!;
+ }
+ }
+ }
+} \ No newline at end of file