start working on assembly loader
to be used with the generated assemblies from the user code.
This commit is contained in:
parent
d80cc51aff
commit
bd76fc1b25
8
Nerfed.Editor/Project/EditorAssemblyLoadContext.cs
Normal file
8
Nerfed.Editor/Project/EditorAssemblyLoadContext.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.Loader;
|
||||||
|
|
||||||
|
namespace Nerfed.Editor.Project;
|
||||||
|
|
||||||
|
internal class EditorAssemblyLoadContext : AssemblyLoadContext
|
||||||
|
{
|
||||||
|
public EditorAssemblyLoadContext() : base(isCollectible: true) { }
|
||||||
|
}
|
109
Nerfed.Editor/Project/EditorAssemblyLoader.cs
Normal file
109
Nerfed.Editor/Project/EditorAssemblyLoader.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
using Nerfed.Runtime;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Nerfed.Editor.Project;
|
||||||
|
|
||||||
|
// https://github.com/godotengine/godot/blob/master/modules/mono/glue/GodotSharp/GodotPlugins/Main.c
|
||||||
|
// https://gitlab.com/robertk92/assemblyreloadtest/-/blob/main/AppContextTest/Program.cs
|
||||||
|
|
||||||
|
internal static class EditorAssemblyLoader
|
||||||
|
{
|
||||||
|
private sealed class EditorAssemblyLoadContextWrapper
|
||||||
|
{
|
||||||
|
private EditorAssemblyLoadContext assemblyLoadContext;
|
||||||
|
private readonly WeakReference weakReference;
|
||||||
|
|
||||||
|
private EditorAssemblyLoadContextWrapper(EditorAssemblyLoadContext assemblyLoadContext, WeakReference weakReference)
|
||||||
|
{
|
||||||
|
this.assemblyLoadContext = assemblyLoadContext;
|
||||||
|
this.weakReference = weakReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCollectible
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
// If assemblyLoadContext is null we already started unloading, so it was collectible.
|
||||||
|
get => assemblyLoadContext?.IsCollectible ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAlive
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
get => weakReference.IsAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static (Assembly, EditorAssemblyLoadContextWrapper) CreateAndLoad(AssemblyName assemblyName)
|
||||||
|
{
|
||||||
|
EditorAssemblyLoadContext context = new EditorAssemblyLoadContext();
|
||||||
|
WeakReference reference = new WeakReference(context, trackResurrection: true);
|
||||||
|
EditorAssemblyLoadContextWrapper wrapper = new EditorAssemblyLoadContextWrapper(context, reference);
|
||||||
|
Assembly assembly = context.LoadFromAssemblyName(assemblyName);
|
||||||
|
return (assembly, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
internal void Unload()
|
||||||
|
{
|
||||||
|
assemblyLoadContext?.Unload();
|
||||||
|
assemblyLoadContext = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (Assembly, EditorAssemblyLoadContextWrapper) Load(string assemblyFilePath)
|
||||||
|
{
|
||||||
|
string assemblyFileName = Path.GetFileNameWithoutExtension(assemblyFilePath);
|
||||||
|
|
||||||
|
AssemblyName assemblyName = new AssemblyName(assemblyFileName);
|
||||||
|
|
||||||
|
return EditorAssemblyLoadContextWrapper.CreateAndLoad(assemblyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Unload(EditorAssemblyLoadContextWrapper assemblyLoadContextWrapper)
|
||||||
|
{
|
||||||
|
if (assemblyLoadContextWrapper == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assemblyLoadContextWrapper.IsCollectible)
|
||||||
|
{
|
||||||
|
Log.Error($"{assemblyLoadContextWrapper} is not collectable!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assemblyLoadContextWrapper.Unload();
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
|
||||||
|
TimeSpan timeout = TimeSpan.FromSeconds(30);
|
||||||
|
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
while (assemblyLoadContextWrapper.IsAlive)
|
||||||
|
{
|
||||||
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
|
||||||
|
if (!assemblyLoadContextWrapper.IsAlive)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopwatch.Elapsed.TotalSeconds % 10 == 0)
|
||||||
|
{
|
||||||
|
Log.Info("Tring to unload assembly...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopwatch.Elapsed >= timeout)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to unload assembly!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user