Skip to content

Commit

Permalink
feat: PluginPriority & unload
Browse files Browse the repository at this point in the history
  • Loading branch information
LazuliKao committed Jul 22, 2023
1 parent 111047f commit 3273213
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 36 deletions.
38 changes: 33 additions & 5 deletions src/EntryPointAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
namespace Loader;

public abstract class EntryPointAttributeBase : Attribute
public enum ModulePriority
{
internal abstract IPlugin CreateInstance();
Library,
Front,
High,
Normal
}

public abstract class ModuleEntryPointAttributeBase
: Attribute,
IComparer<ModuleEntryPointAttributeBase>
{
internal abstract IModule CreateInstance();
internal ModulePriority Priority { get; set; }

//for sort
public int Compare(ModuleEntryPointAttributeBase? x, ModuleEntryPointAttributeBase? y)
{
if (ReferenceEquals(x, y))
return 0;
if (ReferenceEquals(null, y))
return 1;
if (ReferenceEquals(null, x))
return -1;
return x.Priority.CompareTo(y.Priority);
}
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class EntryPointAttribute<T> : EntryPointAttributeBase
where T : IPlugin, new()
public sealed class ModuleEntryPointAttribute<T> : ModuleEntryPointAttributeBase
where T : IModule, new()
{
internal override IPlugin CreateInstance() => new T();
public ModuleEntryPointAttribute(ModulePriority priority = ModulePriority.Normal)
{
Priority = priority;
}

internal override IModule CreateInstance() => new T();
}
3 changes: 2 additions & 1 deletion src/IPlugin.cs → src/IModule.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
namespace Loader;

public interface IPlugin : IDisposable
public interface IModule : IDisposable
{
string Name { get; }
void Initialize();
void Unload();
}
2 changes: 1 addition & 1 deletion src/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ public static void Initialize()
{
directoryInfo.Create();
}
PluginManager.LoadAllPlugin();
PluginManager.ReloadAllPlugin();
}
}
96 changes: 67 additions & 29 deletions src/PluginManager.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,18 @@
using System.IO;
using System.Reflection;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;

namespace Loader;

internal static class PluginManager
{
public static Dictionary<AssemblyLoadContext, List<Action>> PluginContexts { get; }
public static Dictionary<AssemblyLoadContext, List<IModule>> PluginContexts { get; }

static PluginManager()
{
PluginContexts = new();
}

public static bool LoadPlugin(string path)
{
AssemblyLoadContext loadContext = new(path);
Assembly assembly = loadContext.LoadFromAssemblyPath(path);
bool success = false;
List<Action> plugins = new();
foreach (var entryPoint in assembly.GetCustomAttributes<EntryPointAttributeBase>())
{
IPlugin plugin = entryPoint.CreateInstance();
plugin.Initialize();
success = true;
plugins.Add(plugin.Unload);
}
if (!success)
{
loadContext.Unload();
return false;
}
PluginContexts[loadContext] = plugins;
return true;
}

public static IEnumerable<string> EnumerableAllDll()
{
DirectoryInfo directoryInfo = new("plugins");
Expand All @@ -48,22 +26,82 @@ public static IEnumerable<string> EnumerableAllDll()
}
}

public static void LoadAllPlugin()
[MethodImpl(MethodImplOptions.Synchronized)]
public static void ReloadAllPlugin()
{
//uninstall loaded modules
foreach (var (ctx, unload) in PluginContexts)
{
try
{
foreach (var module in unload)
{
try
{
module.Unload();
}
catch (Exception e)
{
Console.Error.WriteLine(
"plugin {0} unload failed. file {1}. {2}",
module.Name,
ctx.Name,
e
);
}
}
ctx.Unload();
}
catch (Exception e)
{
Console.Error.WriteLine("assembly unload failed {0}. {1}", ctx.Name, e);
}
}
//clear PluginContexts
PluginContexts.Clear();
//dict for sort
SortedDictionary<
ModuleEntryPointAttributeBase,
(AssemblyLoadContext ctx, List<IModule> unload)
> allEntryPoint = new();
//enumerate all dll and load all EntryPointAttribute
//then sort by priority
foreach (var file in EnumerableAllDll())
{
try
{
if (LoadPlugin(file))
AssemblyLoadContext loadContext = new(file);
Assembly assembly = loadContext.LoadFromAssemblyPath(file);
foreach (
var entryPoint in assembly.GetCustomAttributes<ModuleEntryPointAttributeBase>()
)
{
continue;
allEntryPoint.Add(entryPoint, new());
}
}
catch (Exception e)
{
Console.Error.WriteLine("{0} load failed. {1}", file, e);
}
}
//load all modules
foreach (var (entryPoint, (_, modules)) in allEntryPoint)
{
try
{
var module = entryPoint.CreateInstance();
module.Initialize(); //initialize module
modules.Add(module);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Error.WriteLine("{0} load failed.", file);
}
//add all modules to PluginContexts
foreach (var (_, (ctx, modules)) in allEntryPoint)
{
PluginContexts.Add(ctx, modules);
}
}
}

0 comments on commit 3273213

Please sign in to comment.