|
@@ -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)
|