Skip to content

Commit

Permalink
Refresh IUpdateable list immediately when waiting for plugin unload. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jhett12321 authored Jan 26, 2025
1 parent 69ed36a commit 652f653
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 15 deletions.
8 changes: 4 additions & 4 deletions NWN.Anvil/src/main/Plugins/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ internal void Load()
}
}

internal WeakReference Unload()
internal WeakReference Unload(bool immediateDispose)
{
RemoveResourceDirectory();
RemoveIsolatedContainer();
RemoveIsolatedContainer(immediateDispose);

Assembly = null;
PluginTypes = null;
Expand Down Expand Up @@ -204,11 +204,11 @@ private void RemoveResourceDirectory()
}
}

private void RemoveIsolatedContainer()
private void RemoveIsolatedContainer(bool immediateDispose)
{
if (Container != null)
{
ServiceManager.DisposePluginContainer(Container, this);
ServiceManager.DisposePluginContainer(Container, this, immediateDispose);
Container = null;
}
}
Expand Down
8 changes: 4 additions & 4 deletions NWN.Anvil/src/main/Plugins/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public WeakReference UnloadPlugin(Plugin plugin, bool waitForUnload = true)
throw new InvalidOperationException($"Non-isolated plugin {plugin.Name} may not be unloaded at runtime.");
}

WeakReference pluginRef = UnloadPluginInternal(plugin);
WeakReference pluginRef = UnloadPluginInternal(plugin, waitForUnload);
if (waitForUnload)
{
WaitForPendingUnloads(new Dictionary<WeakReference, string>
Expand Down Expand Up @@ -201,7 +201,7 @@ void ICoreService.Unload()
foreach (Plugin plugin in Plugins)
{
bool waitForUnload = EnvironmentConfig.ReloadEnabled || plugin.PluginInfo.Isolated;
WeakReference pluginRef = UnloadPluginInternal(plugin);
WeakReference pluginRef = UnloadPluginInternal(plugin, waitForUnload);

if (waitForUnload)
{
Expand Down Expand Up @@ -306,12 +306,12 @@ private void LoadPluginInternal(Plugin plugin)
}
}

private WeakReference UnloadPluginInternal(Plugin plugin)
private WeakReference UnloadPluginInternal(Plugin plugin, bool immediateDispose)
{
if (plugin.IsLoaded)
{
Log.Info("Unloading DotNET plugin {PluginName} - {PluginPath}", plugin.Name.Name, plugin.Path);
return plugin.Unload();
return plugin.Unload(immediateDispose);
}

return new WeakReference(null);
Expand Down
16 changes: 15 additions & 1 deletion NWN.Anvil/src/main/Services/Messages/AnvilMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@ internal sealed class AnvilMessageService : ICoreService
[Inject]
private IServiceManager ServiceManager { get; init; } = null!;

private bool updateTargetsModified;

internal void RunServerLoop()
{
for (int i = 0; i < updateTargets.Count; i++)
{
updateTargets[i].Update();

if (updateTargetsModified)
{
updateTargetsModified = false;
break;
}
}

if (pendingAddTargets.Count > 0 || pendingRemoveTargets.Count > 0)
Expand Down Expand Up @@ -56,14 +64,20 @@ private void OnContainerCreate(IServiceContainer container, Plugin? plugin)
pendingAddTargets.AddRange(container.GetAllInstances<IUpdateable>().OrderBy(service => service.GetType().GetServicePriority()));
}

private void OnContainerDispose(IServiceContainer container, Plugin? plugin)
private void OnContainerDispose(IServiceContainer container, Plugin? plugin, bool immediateDispose)
{
pendingRemoveTargets.AddRange(container.GetAllInstances<IUpdateable>());

foreach (ILateDisposable lateDisposeTarget in container.GetAllInstances<ILateDisposable>())
{
lateDisposeTargets.Add(lateDisposeTarget);
}

if (immediateDispose)
{
UpdateLoopList();
updateTargetsModified = true;
}
}

void ICoreService.Init()
Expand Down
8 changes: 4 additions & 4 deletions NWN.Anvil/src/main/Services/Services/AnvilServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal sealed class AnvilServiceManager : IServiceManager
public ServiceContainer CoreServiceContainer { get; }

public event Action<IServiceContainer, Plugin?>? OnContainerCreate;
public event Action<IServiceContainer, Plugin?>? OnContainerDispose;
public event Action<IServiceContainer, Plugin?, bool>? OnContainerDispose;
public event Action<IServiceContainer, Plugin?>? OnContainerPostDispose;

public AnvilServiceManager()
Expand Down Expand Up @@ -68,9 +68,9 @@ IServiceContainer IServiceManager.CreatePluginContainer(Plugin plugin)
return pluginContainer;
}

void IServiceManager.DisposePluginContainer(IServiceContainer container, Plugin plugin)
void IServiceManager.DisposePluginContainer(IServiceContainer container, Plugin plugin, bool immediateDispose)
{
OnContainerDispose?.Invoke(container, plugin);
OnContainerDispose?.Invoke(container, plugin, immediateDispose);
container.Dispose();
OnContainerPostDispose?.Invoke(container, plugin);
}
Expand Down Expand Up @@ -153,7 +153,7 @@ private void UnloadAnvilServices()
Log.Info("Unloading anvil services...");

// This must always happen in a separate scope/method, otherwise in Debug and some Release configurations, AnvilServiceContainer will hold a strong reference and prevent plugin unload.
OnContainerDispose?.Invoke(AnvilServiceContainer, null);
OnContainerDispose?.Invoke(AnvilServiceContainer, null, true);
AnvilServiceContainer.Dispose();
AnvilServiceContainer = anvilContainerFactory.CreateContainer(CoreServiceContainer);
}
Expand Down
5 changes: 3 additions & 2 deletions NWN.Anvil/src/main/Services/Services/IServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface IServiceManager
/// <summary>
/// Called when a service container is about to be disposed.
/// </summary>
event Action<IServiceContainer, Plugin?> OnContainerDispose;
event Action<IServiceContainer, Plugin?, bool> OnContainerDispose;

/// <summary>
/// Called when a service container has been disposed.
Expand All @@ -52,7 +52,8 @@ public interface IServiceManager
/// </summary>
/// <param name="container">The container to dispose.</param>
/// <param name="plugin">The plugin owning this container.</param>
void DisposePluginContainer(IServiceContainer container, Plugin plugin);
/// <param name="immediate">If the plugin container should complete dispose immediately (true) or at the end of the current frame (false)</param>
void DisposePluginContainer(IServiceContainer container, Plugin plugin, bool immediate);

/// <summary>
/// Called during NWNX initialization. Core services should be initialized here.
Expand Down

0 comments on commit 652f653

Please sign in to comment.