From 4b98f4e60aa9a864886d18240aa95ad6f6617c1b Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Wed, 24 Jul 2024 18:55:27 +0900 Subject: [PATCH] Remove nullability from IServiceScope.CreateAsync --- Dalamud/IoC/Internal/ServiceContainer.cs | 14 ++++++------ Dalamud/IoC/Internal/ServiceScope.cs | 8 +++---- Dalamud/Plugin/DalamudPluginInterface.cs | 24 ++++++++++++-------- Dalamud/Plugin/IDalamudPluginInterface.cs | 11 ++++++++- Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 22 ++++++------------ 5 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index 7a0b4347d..06e2ff14d 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -84,15 +84,15 @@ internal class ServiceContainer : IServiceProvider, IServiceType /// Scoped objects to be included in the constructor. /// The scope to be used to create scoped services. /// The created object. - public async Task CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null) + public async Task CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null) { var scopeImpl = scope as ServiceScopeImpl; var ctor = this.FindApplicableCtor(objectType, scopedObjects); if (ctor == null) { - Log.Error("Failed to create {TypeName}, an eligible ctor with satisfiable services could not be found", objectType.FullName!); - return null; + throw new InvalidOperationException( + $"Failed to create {objectType.FullName ?? objectType.Name}; an eligible ctor with satisfiable services could not be found"); } // validate dependency versions (if they exist) @@ -116,16 +116,16 @@ internal class ServiceContainer : IServiceProvider, IServiceType var hasNull = resolvedParams.Any(p => p == null); if (hasNull) { - Log.Error("Failed to create {TypeName}, a requested service type could not be satisfied", objectType.FullName!); - return null; + throw new InvalidOperationException( + $"Failed to create {objectType.FullName ?? objectType.Name}; a requested service type could not be satisfied"); } var instance = RuntimeHelpers.GetUninitializedObject(objectType); if (!await this.InjectProperties(instance, scopedObjects, scope)) { - Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName!); - return null; + throw new InvalidOperationException( + $"Failed to create {objectType.FullName ?? objectType.Name}; a requested property service type could not be satisfied"); } var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs index c243eca28..33a4d3f54 100644 --- a/Dalamud/IoC/Internal/ServiceScope.cs +++ b/Dalamud/IoC/Internal/ServiceScope.cs @@ -25,7 +25,7 @@ internal interface IServiceScope : IDisposable /// The type of object to create. /// Scoped objects to be included in the constructor. /// The created object. - public Task CreateAsync(Type objectType, params object[] scopedObjects); + public Task CreateAsync(Type objectType, params object[] scopedObjects); /// /// Inject interfaces into public or static properties on the provided object. @@ -45,7 +45,7 @@ internal class ServiceScopeImpl : IServiceScope private readonly ServiceContainer container; private readonly List privateScopedObjects = []; - private readonly ConcurrentDictionary> scopeCreatedObjects = new(); + private readonly ConcurrentDictionary> scopeCreatedObjects = new(); /// /// Initializes a new instance of the class. @@ -63,7 +63,7 @@ internal class ServiceScopeImpl : IServiceScope } /// - public Task CreateAsync(Type objectType, params object[] scopedObjects) + public Task CreateAsync(Type objectType, params object[] scopedObjects) { return this.container.CreateAsync(objectType, scopedObjects, this); } @@ -80,7 +80,7 @@ internal class ServiceScopeImpl : IServiceScope /// The type of object to create. /// Additional scoped objects. /// The created object, or null. - public Task CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) => + public Task CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) => this.scopeCreatedObjects.GetOrAdd( objectType, static (objectType, p) => p.Scope.container.CreateAsync( diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index bb92b6b0c..9fb73fbe1 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Dalamud.Configuration; using Dalamud.Configuration.Internal; @@ -26,6 +27,8 @@ using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; +using Serilog; + namespace Dalamud.Plugin; /// @@ -458,21 +461,22 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #region Dependency Injection - /// - /// Create a new object of the provided type using its default constructor, then inject objects and properties. - /// - /// Objects to inject additionally. - /// The type to create. - /// The created and initialized type. + /// public T? Create(params object[] scopedObjects) where T : class { - var svcContainer = Service.Get(); + var t = this.CreateAsync(scopedObjects); + t.Wait(); - return (T)this.plugin.ServiceScope!.CreateAsync( - typeof(T), - this.GetPublicIocScopes(scopedObjects)).GetAwaiter().GetResult(); + if (t.Exception is { } e) + Log.Error(e, "{who}: Failed to initialize {what}", this.plugin.Name, typeof(T).FullName ?? typeof(T).Name); + + return t.IsCompletedSuccessfully ? t.Result : null; } + /// + public async Task CreateAsync(params object[] scopedObjects) where T : class => + (T)await this.plugin.ServiceScope!.CreateAsync(typeof(T), this.GetPublicIocScopes(scopedObjects)); + /// /// Inject services into properties on the provided object instance. /// diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index 6393dc5ab..b5257c033 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Threading.Tasks; using Dalamud.Configuration; using Dalamud.Game.Text; @@ -304,9 +305,17 @@ public interface IDalamudPluginInterface /// /// Objects to inject additionally. /// The type to create. - /// The created and initialized type. + /// The created and initialized type, or null on failure. T? Create(params object[] scopedObjects) where T : class; + /// + /// Create a new object of the provided type using its default constructor, then inject objects and properties. + /// + /// Objects to inject additionally. + /// The type to create. + /// A task representing the created and initialized type. + Task CreateAsync(params object[] scopedObjects) where T : class; + /// /// Inject services into properties on the provided object instance. /// diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index f7bb3495c..00fa9d243 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -417,24 +417,16 @@ internal class LocalPlugin : IDisposable try { - if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1) - { - var newInstance = await framework.RunOnFrameworkThread( - () => this.ServiceScope.CreateAsync( - this.pluginType!, - this.DalamudInterface!)).ConfigureAwait(false); - - this.instance = newInstance as IDalamudPlugin; - } - else - { - this.instance = - await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin; - } + var forceFrameworkThread = this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1; + var newInstanceTask = forceFrameworkThread ? framework.RunOnFrameworkThread(Create) : Create(); + this.instance = await newInstanceTask.ConfigureAwait(false); + + async Task Create() => + (IDalamudPlugin)await this.ServiceScope!.CreateAsync(this.pluginType!, this.DalamudInterface!); } catch (Exception ex) { - Log.Error(ex, "Exception in plugin constructor"); + Log.Error(ex, "Exception during plugin initialization"); this.instance = null; }