Fix ServiceScope.CreatePrivateScopedObject concurrency

This commit is contained in:
Soreepeong 2024-07-24 18:39:24 +09:00
parent 4383a5747d
commit 32b24b3b5a

View file

@ -1,7 +1,10 @@
using System.Collections.Generic; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Serilog;
namespace Dalamud.IoC.Internal; namespace Dalamud.IoC.Internal;
/// <summary> /// <summary>
@ -41,8 +44,8 @@ internal class ServiceScopeImpl : IServiceScope
{ {
private readonly ServiceContainer container; private readonly ServiceContainer container;
private readonly List<object> privateScopedObjects = new(); private readonly List<object> privateScopedObjects = [];
private readonly List<object> scopeCreatedObjects = new(); private readonly ConcurrentDictionary<Type, Task<object?>> scopeCreatedObjects = new();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ServiceScopeImpl" /> class. /// Initializes a new instance of the <see cref="ServiceScopeImpl" /> class.
@ -77,26 +80,30 @@ internal class ServiceScopeImpl : IServiceScope
/// <param name="objectType">The type of object to create.</param> /// <param name="objectType">The type of object to create.</param>
/// <param name="scopedObjects">Additional scoped objects.</param> /// <param name="scopedObjects">Additional scoped objects.</param>
/// <returns>The created object, or null.</returns> /// <returns>The created object, or null.</returns>
public async Task<object?> CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) public Task<object?> CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) =>
{ this.scopeCreatedObjects.GetOrAdd(
var instance = this.scopeCreatedObjects.FirstOrDefault(x => x.GetType() == objectType); objectType,
if (instance != null) static (objectType, p) => p.Scope.container.CreateAsync(
return instance; objectType,
p.Objects.Concat(p.Scope.privateScopedObjects).ToArray()),
instance = (Scope: this, Objects: scopedObjects));
await this.container.CreateAsync(objectType, scopedObjects.Concat(this.privateScopedObjects).ToArray());
if (instance != null)
this.scopeCreatedObjects.Add(instance);
return instance;
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
foreach (var createdObject in this.scopeCreatedObjects) foreach (var objectTask in this.scopeCreatedObjects)
{ {
switch (createdObject) objectTask.Value.ContinueWith(
static r =>
{
if (!r.IsCompletedSuccessfully)
{
if (r.Exception is { } e)
Log.Error(e, "{what}: Failed to load.", nameof(ServiceScopeImpl));
return;
}
switch (r.Result)
{ {
case IInternalDisposableService d: case IInternalDisposableService d:
d.DisposeService(); d.DisposeService();
@ -105,6 +112,7 @@ internal class ServiceScopeImpl : IServiceScope
d.Dispose(); d.Dispose();
break; break;
} }
});
} }
} }
} }