Math.NET Numerics
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

163 lines
8.0 KiB

// (c) Microsoft Corporation 2005-2009.
namespace Microsoft.FSharp.Math.Experimental
// NOTE: Abstractable code.
// This file is a Service/Provider model cache with pre-checks that supporting DLLs are available.
// This is not LAPACK specific, and is more generally useful.
// It has been abstracted over the underlying service type (ILapack becomes 'a).
// The providers could have other pre-conditions, e.g. machine architecture, OS, etc.
open Microsoft.FSharp.Collections
open System.IO
open System.Collections.Generic
/// Generic provider with unmanaged DLL dependencies.
type Provider<'a>(name:string,requiredDLLs:string[],provide:unit -> 'a) =
// NOTE: The dependencies could be extended to include architecture.
member this.Name = name
member this.RequiredDLLs = requiredDLLs
member this.Provide() = provide()
module Locals =
let mkProvider (name,requireDLLs,provide) = Provider(name,requireDLLs,provide)
let noRepeats xs = xs |> Set.of_list |> Set.to_list
let noLaterRepeats xs =
let collect (soFar,revXs) x =
if Set.contains x soFar then (soFar,revXs) else (Set.add x soFar,x::revXs)
let (soFar,revXs) = List.fold collect (Set.empty,[]) xs
List.rev revXs
let searchPathsForDLLImports() =
// The providers have DLL dependencies via their platform-invoke DLLImport attributes.
// The DLLs search procedure for those DLLs is described below. (follow links).
// Here, we compute those paths (a superset of them).
//
// From: VC++ Programming Guide: Search Path Used by Windows to Locate a DLL
// http://msdn2.microsoft.com/en-us/library/7d83bc18(VS.80).aspx
//
// Search order:
// The directory where the executable module for the current process is located.
// The current directory.
// The Windows system directory. The GetSystemDirectory function retrieves the path of this directory.
// The Windows directory. The GetWindowsDirectory function retrieves the path of this directory.
// The directories listed in the PATH environment variable.
//
// From: Development Impacts of Security Changes in Windows Server 2003
// http://msdn2.microsoft.com/en-us/library/ms972822.aspx
// This reports that system directories are now searched first.
//
let windowsSystemDir = System.Environment.SystemDirectory
let windowsDir = System.IO.Path.GetDirectoryName(windowsSystemDir)
let currentExeDirs =
// This includes EXE directory, and loaded DLL directories.
// It may be an over-estimate of the search path.
let proc = System.Diagnostics.Process.GetCurrentProcess()
let mods : System.Diagnostics.ProcessModule list = proc.Modules |> Seq.cast |> Seq.to_list
mods |> List.map (fun m -> Path.GetDirectoryName m.FileName) |> noRepeats
let currentDir = System.Environment.CurrentDirectory
let pathDirs =
match System.Environment.GetEnvironmentVariable("PATH") with
| null -> []
| paths -> paths.Split([|';'|]) |> Array.to_list
let orderedSearchPaths = windowsSystemDir :: windowsDir :: (currentExeDirs @ [currentDir] @ pathDirs)
noLaterRepeats orderedSearchPaths
let pathDLLs (path:string) = if not (Directory.Exists path) then [||] else Directory.GetFiles(path,"*.DLL")
let dllFilename (dll:string) = (Path.GetFileName dll).ToLower() // normalizes filename
let loadableDLLPaths() =
// Makes a table of (DLL,availablePaths)
// This is reusable code...
let paths = searchPathsForDLLImports()
let dllPaths = new Dictionary<string,ResizeArray<string>>()
let add path dll =
let dll = dllFilename dll
if not (dllPaths.ContainsKey(dll)) then dllPaths.[dll] <- new ResizeArray<_>()
dllPaths.[dll].Add(path)
List.iter (fun path -> Array.iter (add path) (pathDLLs path)) paths
dllPaths
let loadableProvider (dllPaths:Dictionary<_,_>) (provider:Provider<'a>) =
let dllAvailable (dll:string) = dllPaths.ContainsKey(dllFilename dll)
let availableReason (dll:string) = let quote s = "'" ^ s ^ "'"
let paths = dllPaths.[dllFilename dll] |> ResizeArray.to_list
let pathNote path = sprintf "Required %s seen in %s" dll path
System.String.Join("\n", Array.of_list(List.map pathNote paths))
if Array.forall dllAvailable provider.RequiredDLLs then
let justification = System.String.Join("\n", Array.map availableReason provider.RequiredDLLs)
let justification = "Provider: " ^ provider.Name ^ "\n" ^ justification
Some (provider,justification)
else
None
let checkProvider p =
match loadableProvider (loadableDLLPaths()) p with
| None -> "Provider is not loadable"
| Some (p,justification) -> justification
// This type is internal to Service (motivates nested types).
type 'a ServiceState =
| ServiceDisabled // service disabled, do not look for it.
| ServiceEnabledUninitialised // service enabled, but no search made yet.
| ServiceEnabledOK of 'a * string // service enabled, and justification string for diagnostics.
| ServiceEnabledFailed // service enabled, but DLLs not found, or load failed.
open Locals
type Provider<'a> with
member this.Check() = checkProvider this
type Service<'a>(providers:Provider<'a> seq) =
let mutable providers = Seq.to_array providers // possible providers configuration state
let mutable state = ServiceEnabledUninitialised // service state
/// Service Providers
member this.Providers with get() = providers
and set(x) = providers <- x
/// Disable the service.
member this.Stop() = state <- ServiceDisabled
/// Use the LAPACK service from the given provider.
/// If the supporting DLLs are not available, this may fail (now or later).
member this.StartWith(p:Provider<'a>) =
let justification =
match loadableProvider (loadableDLLPaths()) p with
| None -> "The provider DLLs did not appear to be present, the service may fail"
| Some (p,justification) -> justification
state <- ServiceEnabledOK (p.Provide(),justification)
/// Start the service with the first provider that looks loadable.
member this.Start() =
let candidates = Array.choose (loadableProvider (loadableDLLPaths())) providers
if candidates.Length=0 then // guard
state <- ServiceEnabledFailed
false
else
let provider,justification = candidates.[0]
state <- ServiceEnabledOK (provider.Provide(),justification) // index covered by guard above
true
member this.Service() =
if state = ServiceEnabledUninitialised then this.Start() |> ignore
match state with
| ServiceDisabled
| ServiceEnabledUninitialised // (The above initialisation call must have failed)
| ServiceEnabledFailed -> None
| ServiceEnabledOK (service,justification) -> Some service
member this.Available() =
match state with
| ServiceDisabled
| ServiceEnabledFailed
| ServiceEnabledUninitialised -> false
| ServiceEnabledOK (service,justification) -> true
member this.Status() =
match state with
| ServiceDisabled -> "Disabled"
| ServiceEnabledFailed -> "Failed to start"
| ServiceEnabledUninitialised -> "Will auto enable on demand"
| ServiceEnabledOK (service,justification) -> "Enabled\n" ^ justification