pm: make sure that interface and scope are always disposed if ctor throws

This fixes an issue wherein dev plugins could end up in a half-disposed state if the ctor were to throw an exception
This commit is contained in:
goat 2024-06-08 21:44:25 +02:00
parent 625a71a6d1
commit cf4ecacf85

View file

@ -412,28 +412,39 @@ internal class LocalPlugin : IDisposable
this.ServiceScope = ioc.GetScope(); this.ServiceScope = ioc.GetScope();
this.ServiceScope.RegisterPrivateScopes(this); // Add this LocalPlugin as a private scope, so services can get it this.ServiceScope.RegisterPrivateScopes(this); // Add this LocalPlugin as a private scope, so services can get it
if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1) try
{ {
this.instance = await framework.RunOnFrameworkThread( if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1)
() => this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!)) as IDalamudPlugin; {
this.instance = await framework.RunOnFrameworkThread(
() => this.ServiceScope.CreateAsync(
this.pluginType!,
this.DalamudInterface!)) as IDalamudPlugin;
}
else
{
this.instance =
await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin;
}
} }
else catch (Exception ex)
{ {
this.instance = Log.Error(ex, "Exception in plugin constructor");
await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin; this.instance = null;
} }
if (this.instance == null) if (this.instance == null)
{ {
this.State = PluginState.LoadError; this.State = PluginState.LoadError;
this.DalamudInterface.DisposeInternal(); this.UnloadAndDisposeState();
Log.Error( Log.Error(
$"Error while loading {this.Name}, failed to bind and call the plugin constructor"); "Error while loading {PluginName}, failed to bind and call the plugin constructor", this.InternalName);
return; return;
} }
this.State = PluginState.Loaded; this.State = PluginState.Loaded;
Log.Information($"Finished loading {this.DllFile.Name}"); Log.Information("Finished loading {PluginName}", this.InternalName);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -443,7 +454,7 @@ internal class LocalPlugin : IDisposable
if (ex is PluginPreconditionFailedException) if (ex is PluginPreconditionFailedException)
Log.Warning(ex.Message); Log.Warning(ex.Message);
else else
Log.Error(ex, $"Error while loading {this.Name}"); Log.Error(ex, "Error while loading {PluginName}", this.InternalName);
throw; throw;
} }
@ -498,15 +509,7 @@ internal class LocalPlugin : IDisposable
await framework.RunOnFrameworkThread(() => this.instance?.Dispose()); await framework.RunOnFrameworkThread(() => this.instance?.Dispose());
this.instance = null; this.instance = null;
this.UnloadAndDisposeState();
this.DalamudInterface?.DisposeInternal();
this.DalamudInterface = null;
this.ServiceScope?.Dispose();
this.ServiceScope = null;
this.pluginType = null;
this.pluginAssembly = null;
if (!reloading) if (!reloading)
{ {
@ -681,4 +684,19 @@ internal class LocalPlugin : IDisposable
throw new InvalidPluginException(this.DllFile); throw new InvalidPluginException(this.DllFile);
} }
} }
private void UnloadAndDisposeState()
{
if (this.instance != null)
throw new InvalidOperationException("Plugin instance should be disposed at this point");
this.DalamudInterface?.DisposeInternal();
this.DalamudInterface = null;
this.ServiceScope?.Dispose();
this.ServiceScope = null;
this.pluginType = null;
this.pluginAssembly = null;
}
} }