diff options
author | vman <public@vaughnnugent.com> | 2022-11-16 14:07:28 -0500 |
---|---|---|
committer | vman <public@vaughnnugent.com> | 2022-11-16 14:07:28 -0500 |
commit | 3fb601d14354c867e1ead94b027c99c4a2fc15b5 (patch) | |
tree | 5bf01312166d97eff255d1fdcd26bf314cebcf76 /VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs | |
parent | c3419e3e43f773ba9ee1e4854e15da873829fbd7 (diff) |
Add project files.
Diffstat (limited to 'VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs')
-rw-r--r-- | VNLib.Plugins.Extensions.Loading/AssemblyLoader.cs | 101 |
1 files changed, 101 insertions, 0 deletions
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 +{ + /// <summary> + /// <para> + /// Represents a disposable assembly loader wrapper for + /// exporting a signle type from a loaded assembly + /// </para> + /// <para> + /// If the loaded type implements <see cref="IDisposable"/> the + /// dispose method is called when the loader is disposed + /// </para> + /// </summary> + /// <typeparam name="T">The exported type to manage</typeparam> + public class AssemblyLoader<T> : OpenResourceHandle<T> + { + private readonly PluginLoader _loader; + private readonly Type _typeInfo; + private readonly CancellationTokenRegistration _reg; + private readonly Lazy<T> _instance; + + /// <summary> + /// The instance of the loaded type + /// </summary> + 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); + } + /// <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> + 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)!; + } + + ///<inheritdoc/> + 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(); + } + + /// <summary> + /// Creates a new assembly loader for the specified type and + /// </summary> + /// <param name="assemblyName">The name of the assmbly within the current plugin directory</param> + /// <param name="unloadToken">The plugin unload token</param> + internal static AssemblyLoader<T> 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); + } + } +} |