From 3fb601d14354c867e1ead94b027c99c4a2fc15b5 Mon Sep 17 00:00:00 2001 From: vman Date: Wed, 16 Nov 2022 14:07:28 -0500 Subject: Add project files. --- VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs | 101 +++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs (limited to 'VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs') diff --git a/VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs b/VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs new file mode 100644 index 0000000..9c09b3f --- /dev/null +++ b/VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs @@ -0,0 +1,101 @@ +using System; +using System.Linq; +using System.Threading; +using System.Reflection; + +using McMaster.NETCore.Plugins; + +using VNLib.Utils; +using System.Runtime.Loader; + +namespace VNLib.Plugins.Extensions.Loading +{ + /// + /// + /// Represents a disposable assembly loader wrapper for + /// exporting a signle type from a loaded assembly + /// + /// + /// If the loaded type implements the + /// dispose method is called when the loader is disposed + /// + /// + /// The exported type to manage + public class AssemblyLoader : OpenResourceHandle + { + private readonly PluginLoader _loader; + private readonly Type _typeInfo; + private readonly CancellationTokenRegistration _reg; + private readonly Lazy _instance; + + /// + /// The instance of the loaded type + /// + public override T Resource => _instance.Value; + + private AssemblyLoader(PluginLoader loader, in CancellationToken unloadToken) + { + _typeInfo = typeof(T); + _loader = loader; + //Init lazy loader + _instance = new(LoadAndGetExportedType, LazyThreadSafetyMode.PublicationOnly); + //Register dispose + _reg = unloadToken.Register(Dispose); + } + /// + /// Loads the default assembly and gets the expected export type, + /// creates a new instance, and calls its parameterless constructor + /// + /// The desired type instance + /// + private T LoadAndGetExportedType() + { + //Load the assembly + Assembly asm = _loader.LoadDefaultAssembly(); + + //See if the type is exported + Type exp = (from type in asm.GetExportedTypes() + where _typeInfo.IsAssignableFrom(type) + select type) + .FirstOrDefault() + ?? throw new EntryPointNotFoundException($"Imported assembly does not export type {_typeInfo.FullName}"); + //Create instance + return (T)Activator.CreateInstance(exp)!; + } + + /// + protected override void Free() + { + //If the instance is disposable, call its dispose method on unload + if (_instance.IsValueCreated && _instance.Value is IDisposable) + { + (_instance.Value as IDisposable)?.Dispose(); + } + _loader.Dispose(); + _reg.Dispose(); + } + + /// + /// Creates a new assembly loader for the specified type and + /// + /// The name of the assmbly within the current plugin directory + /// The plugin unload token + internal static AssemblyLoader Load(string assemblyName, CancellationToken unloadToken) + { + PluginConfig conf = new(assemblyName) + { + IsUnloadable = true, + EnableHotReload = false, + PreferSharedTypes = true, + DefaultContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? AssemblyLoadContext.Default + }; + + PluginLoader loader = new(conf); + + //Add the assembly the type originaged from + conf.SharedAssemblies.Add(typeof(T).Assembly.GetName()); + + return new(loader, in unloadToken); + } + } +} -- cgit