From a112a2a380f6741fd3fc4e2c36cb1e3c8fefaff4 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Fri, 20 Mar 2015 17:15:09 +0100 Subject: [PATCH] Native Provider Loader: optionally set directory from the outside via Control.NativeProviderPath --- src/Numerics/Control.cs | 6 + .../Mkl/MklLinearAlgebraProvider.cs | 2 +- .../Providers/NativeProviderLoader.cs | 130 +++++++++++++----- 3 files changed, 101 insertions(+), 37 deletions(-) diff --git a/src/Numerics/Control.cs b/src/Numerics/Control.cs index 1632b53c..ee2c9d4e 100644 --- a/src/Numerics/Control.cs +++ b/src/Numerics/Control.cs @@ -30,6 +30,7 @@ using MathNet.Numerics.Providers.LinearAlgebra; using System; +using System.IO; using System.Threading.Tasks; namespace MathNet.Numerics @@ -144,6 +145,11 @@ namespace MathNet.Numerics /// public static bool ThreadSafeRandomNumberGenerators { get; set; } + /// + /// Optional path to try to load native provider binaries from. + /// + public static DirectoryInfo NativeProviderPath { get; set; } + /// /// Gets or sets the linear algebra provider. Consider to use UseNativeMKL or UseManaged instead. /// diff --git a/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs b/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs index 5fe07ce2..33ee212e 100644 --- a/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs +++ b/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs @@ -138,7 +138,7 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Mkl try { // Load the native library - NativeProviderLoader.LoadNativeLibrary(SafeNativeMethods.DllName); + NativeProviderLoader.TryLoad(SafeNativeMethods.DllName); a = SafeNativeMethods.query_capability(0); b = SafeNativeMethods.query_capability(1); diff --git a/src/Numerics/Providers/NativeProviderLoader.cs b/src/Numerics/Providers/NativeProviderLoader.cs index 3a8c2b5b..f9e19c40 100644 --- a/src/Numerics/Providers/NativeProviderLoader.cs +++ b/src/Numerics/Providers/NativeProviderLoader.cs @@ -42,34 +42,43 @@ namespace MathNet.Numerics.Providers /// /// Helper class to load native libraries depending on the architecture of the OS and process. /// - static internal class NativeProviderLoader + internal static class NativeProviderLoader { - private static Lazy> _ArchitectureDirectories - = new Lazy>( - () => new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {"x86", "x86"}, - {"AMD64", "x64"}, - {"IA64", "ia64"}, - {"ARM", "arm"} - }, - true); + static Lazy> _ArchitectureDirectories + = new Lazy>( + () => new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "x86", "x86" }, + { "AMD64", "x64" }, + { "IA64", "ia64" }, + { "ARM", "arm" } + }, + true); + /// /// Default directories for each architecture. /// - private static Dictionary ArchitectureDirectories { get { return _ArchitectureDirectories.Value; } } + static Dictionary ArchitectureDirectories + { + get { return _ArchitectureDirectories.Value; } + } + + static Lazy> _nativeHandles = new Lazy>(true); - private static Lazy> _nativeHandles = new Lazy>(true); /// /// Dictionary of handles to previously loaded libraries, /// - private static Dictionary NativeHandles { get { return _nativeHandles.Value; } } + static Dictionary NativeHandles + { + get { return _nativeHandles.Value; } + } + + static string _ArchDirectory = null; - private static string _ArchDirectory = null; /// /// Gets a string indicating the architecture and bitness of the current process. /// - private static string ArchDirectory + static string ArchDirectory { get { @@ -99,7 +108,7 @@ namespace MathNet.Numerics.Providers } } - private static bool IsUnix + static bool IsUnix { get { @@ -114,33 +123,82 @@ namespace MathNet.Numerics.Providers /// public static Exception LastException { get; private set; } - private static object staticLock = new Object(); + static readonly object StaticLock = new Object(); /// /// Load the native library with the given filename. /// /// The file name of the library to load. /// True if the library was successfully loaded or if it has already been loaded. - public static bool LoadNativeLibrary(string fileName) + public static bool TryLoad(string fileName) { if (string.IsNullOrEmpty(fileName)) + { throw new ArgumentNullException("fileName"); + } - lock (staticLock) + // If we have an extra path provided by the user, look there first + var userDir = Control.NativeProviderPath; + if (userDir != null && userDir.Exists && TryLoad(fileName, userDir)) { - IntPtr libraryHandle = IntPtr.Zero; - if (NativeHandles.TryGetValue(fileName, out libraryHandle)) - return true; + return true; + } - if (string.IsNullOrEmpty(ArchDirectory)) - return false; + // Look under the current AppDomain's base directory + if (TryLoad(fileName, new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory))) + { + return true; + } + + // Look at this assembly's directory + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + if (!string.IsNullOrEmpty(assemblyDir) && TryLoad(fileName, new DirectoryInfo(assemblyDir))) + { + return true; + } + + return false; + } - // Look for the library in a acrhtecture directory under the current AppDomain's base directory or this assembly's directory - string path = Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory), ArchDirectory, fileName); - if (!File.Exists(path)) - path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), ArchDirectory, fileName); + /// + /// Try to load a native library by providing its name and a directory. + /// Tries to load an implementation suitable for the current CPU architecture + /// and process mode if there is a matching subfolder. + /// + /// True if the library was successfully loaded or if it has already been loaded. + public static bool TryLoad(string fileName, DirectoryInfo directory) + { + if (!directory.Exists) + { + return false; + } + + // If we have a know architecture, try the matching subdirectory first + var architecture = ArchDirectory; + if (!string.IsNullOrEmpty(architecture) && TryLoadFile(new FileInfo(Path.Combine(directory.FullName, architecture, fileName)))) + { + return true; + } + + // Otherwise try to load directly from the provided directory + return TryLoadFile(new FileInfo(Path.Combine(directory.FullName, fileName))); + } + + /// + /// Try to load a native library by providing the full path including the file name of the library. + /// + /// True if the library was successfully loaded or if it has already been loaded. + public static bool TryLoadFile(FileInfo file) + { + lock (StaticLock) + { + IntPtr libraryHandle; + if (NativeHandles.TryGetValue(file.Name, out libraryHandle)) + { + return true; + } - if (!File.Exists(path)) + if (!file.Exists) { // If the library isn't found within an architecture specific folder then return false // to allow normal P/Invoke searching behaviour when the library is called @@ -148,7 +206,7 @@ namespace MathNet.Numerics.Providers } // If successful this will return a handle to the library - libraryHandle = IsUnix ? UnixLoader.LoadLibrary(path) : WindowsLoader.LoadLibrary(path); + libraryHandle = IsUnix ? UnixLoader.LoadLibrary(file.FullName) : WindowsLoader.LoadLibrary(file.FullName); if (libraryHandle == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); @@ -158,7 +216,7 @@ namespace MathNet.Numerics.Providers else { LastException = null; - NativeHandles[fileName] = libraryHandle; + NativeHandles[file.Name] = libraryHandle; } return libraryHandle != IntPtr.Zero; @@ -167,7 +225,7 @@ namespace MathNet.Numerics.Providers [SuppressUnmanagedCodeSecurity] [SecurityCritical] - private static class WindowsLoader + static class WindowsLoader { public static IntPtr LoadLibrary(string fileName) { @@ -178,12 +236,12 @@ namespace MathNet.Numerics.Providers const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008; [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr LoadLibraryEx(string fileName, IntPtr reservedNull, uint flags); + static extern IntPtr LoadLibraryEx(string fileName, IntPtr reservedNull, uint flags); } [SuppressUnmanagedCodeSecurity] [SecurityCritical] - private static class UnixLoader + static class UnixLoader { public static IntPtr LoadLibrary(string fileName) { @@ -193,7 +251,7 @@ namespace MathNet.Numerics.Providers const int RTLD_NOW = 2; [DllImport("libdl.so", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr dlopen(String fileName, int flags); + static extern IntPtr dlopen(String fileName, int flags); } } }