1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-10 13:00:37 +00:00

C#: Fix editor integration breaking and causing error spam when reloading assemblies fails

- Do not reload scripts from non-collectible assemblies
- Do not load GodotTools as collectible
- Do not attempt to reload the same project assembly forever
This commit is contained in:
RedworkDE
2023-03-31 11:53:16 +02:00
parent 58fae90ff3
commit e0f644a48d
12 changed files with 142 additions and 43 deletions

View File

@@ -21,6 +21,13 @@ namespace GodotPlugins
private sealed class PluginLoadContextWrapper
{
private PluginLoadContext? _pluginLoadContext;
private readonly WeakReference _weakReference;
private PluginLoadContextWrapper(PluginLoadContext pluginLoadContext, WeakReference weakReference)
{
_pluginLoadContext = pluginLoadContext;
_weakReference = weakReference;
}
public string? AssemblyLoadedPath
{
@@ -31,7 +38,14 @@ namespace GodotPlugins
public bool IsCollectible
{
[MethodImpl(MethodImplOptions.NoInlining)]
get => _pluginLoadContext?.IsCollectible ?? false;
// if _pluginLoadContext is null we already started unloading, so it was collectible
get => _pluginLoadContext?.IsCollectible ?? true;
}
public bool IsAlive
{
[MethodImpl(MethodImplOptions.NoInlining)]
get => _weakReference.IsAlive;
}
[MethodImpl(MethodImplOptions.NoInlining)]
@@ -43,19 +57,13 @@ namespace GodotPlugins
bool isCollectible
)
{
var wrapper = new PluginLoadContextWrapper();
wrapper._pluginLoadContext = new PluginLoadContext(
pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
var context = new PluginLoadContext(pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
var reference = new WeakReference(context, trackResurrection: true);
var wrapper = new PluginLoadContextWrapper(context, reference);
var assembly = context.LoadFromAssemblyName(assemblyName);
return (assembly, wrapper);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public WeakReference CreateWeakReference()
{
return new WeakReference(_pluginLoadContext, trackResurrection: true);
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal void Unload()
{
@@ -165,7 +173,7 @@ namespace GodotPlugins
if (_editorApiAssembly == null)
throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: false);
NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
@@ -236,32 +244,29 @@ namespace GodotPlugins
Console.WriteLine("Unloading assembly load context...");
var alcWeakReference = pluginLoadContext.CreateWeakReference();
pluginLoadContext.Unload();
pluginLoadContext = null;
int startTimeMs = Environment.TickCount;
bool takingTooLong = false;
while (alcWeakReference.IsAlive)
while (pluginLoadContext.IsAlive)
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
if (!alcWeakReference.IsAlive)
if (!pluginLoadContext.IsAlive)
break;
int elapsedTimeMs = Environment.TickCount - startTimeMs;
if (!takingTooLong && elapsedTimeMs >= 2000)
if (!takingTooLong && elapsedTimeMs >= 200)
{
takingTooLong = true;
// TODO: How to log from GodotPlugins? (delegate pointer?)
Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
}
else if (elapsedTimeMs >= 5000)
else if (elapsedTimeMs >= 1000)
{
// TODO: How to log from GodotPlugins? (delegate pointer?)
Console.Error.WriteLine(
@@ -273,6 +278,7 @@ namespace GodotPlugins
Console.WriteLine("Assembly load context unloaded successfully.");
pluginLoadContext = null;
return true;
}
catch (Exception e)