mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
fix: scoped services must register their dependencies with PluginManager to ensure the backing services are kept alive long enough
This commit is contained in:
parent
e9e234b340
commit
4b9de31240
3 changed files with 72 additions and 33 deletions
|
|
@ -53,7 +53,6 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
}
|
}
|
||||||
|
|
||||||
this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T));
|
this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T));
|
||||||
this.RegisterInterfaces(typeof(T));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -145,12 +145,12 @@ internal static class ServiceManager
|
||||||
if (serviceKind is ServiceKind.None)
|
if (serviceKind is ServiceKind.None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Scoped service do not go through Service<T>, so we must let ServiceContainer know what their interfaces map to
|
// Let IoC know about the interfaces this service implements
|
||||||
if (serviceKind is ServiceKind.ScopedService)
|
serviceContainer.RegisterInterfaces(serviceType);
|
||||||
{
|
|
||||||
serviceContainer.RegisterInterfaces(serviceType);
|
// Scoped service do not go through Service<T> and are never early loaded
|
||||||
|
if (serviceKind.HasFlag(ServiceKind.ScopedService))
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(
|
Debug.Assert(
|
||||||
!serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService),
|
!serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService),
|
||||||
|
|
@ -176,15 +176,10 @@ internal static class ServiceManager
|
||||||
earlyLoadingServices.Add(serviceType);
|
earlyLoadingServices.Add(serviceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyServicesMap[serviceType] =
|
var typeAsServiceT = ServiceHelpers.GetAsService(serviceType);
|
||||||
(List<Type>)typeof(Service<>)
|
dependencyServicesMap[serviceType] = ServiceHelpers.GetDependencies(typeAsServiceT)
|
||||||
.MakeGenericType(serviceType)
|
.Select(x => typeof(Service<>).MakeGenericType(x))
|
||||||
.InvokeMember(
|
.ToList();
|
||||||
"GetDependencyServices",
|
|
||||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
|
|
@ -327,16 +322,8 @@ internal static class ServiceManager
|
||||||
|
|
||||||
Log.Verbose("Calling GetDependencyServices for '{ServiceName}'", serviceType.FullName!);
|
Log.Verbose("Calling GetDependencyServices for '{ServiceName}'", serviceType.FullName!);
|
||||||
|
|
||||||
dependencyServicesMap[serviceType] =
|
var typeAsServiceT = ServiceHelpers.GetAsService(serviceType);
|
||||||
((List<Type>)typeof(Service<>)
|
dependencyServicesMap[serviceType] = ServiceHelpers.GetDependencies(typeAsServiceT);
|
||||||
.MakeGenericType(serviceType)
|
|
||||||
.InvokeMember(
|
|
||||||
"GetDependencyServices",
|
|
||||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null))!
|
|
||||||
.Select(x => x.GetGenericArguments()[0]).ToList();
|
|
||||||
|
|
||||||
allToUnload.Add(serviceType);
|
allToUnload.Add(serviceType);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -133,8 +134,8 @@ internal static class Service<T> where T : IServiceType
|
||||||
|
|
||||||
res.AddRange(typeof(T)
|
res.AddRange(typeof(T)
|
||||||
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
.Select(x => x.FieldType)
|
.Where(x => x.GetCustomAttribute<ServiceManager.ServiceDependency>(true) != null)
|
||||||
.Where(x => x.GetCustomAttribute<ServiceManager.ServiceDependency>(true) != null));
|
.Select(x => x.FieldType));
|
||||||
|
|
||||||
res.AddRange(typeof(T)
|
res.AddRange(typeof(T)
|
||||||
.GetCustomAttributes()
|
.GetCustomAttributes()
|
||||||
|
|
@ -149,12 +150,31 @@ internal static class Service<T> where T : IServiceType
|
||||||
if (!serviceType.IsAssignableTo(typeof(IServiceType)))
|
if (!serviceType.IsAssignableTo(typeof(IServiceType)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var attr = serviceType.GetCustomAttribute<PluginInterfaceAttribute>(true);
|
if (serviceType == typeof(PluginManager))
|
||||||
if (attr == null)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Scoped plugin services lifetime is tied to their scopes. They go away when LocalPlugin goes away.
|
// Scoped plugin services lifetime is tied to their scopes. They go away when LocalPlugin goes away.
|
||||||
|
// Nonetheless, their direct dependencies must be considered.
|
||||||
if (serviceType.GetServiceKind() == ServiceManager.ServiceKind.ScopedService)
|
if (serviceType.GetServiceKind() == ServiceManager.ServiceKind.ScopedService)
|
||||||
|
{
|
||||||
|
var typeAsServiceT = ServiceHelpers.GetAsService(serviceType);
|
||||||
|
var dependencies = ServiceHelpers.GetDependencies(typeAsServiceT);
|
||||||
|
ServiceManager.Log.Verbose("Found dependencies of scoped plugin service {Type} ({Cnt})", serviceType.FullName!, dependencies!.Count);
|
||||||
|
|
||||||
|
foreach (var scopedDep in dependencies)
|
||||||
|
{
|
||||||
|
if (scopedDep == typeof(PluginManager))
|
||||||
|
throw new Exception("Scoped plugin services cannot depend on PluginManager.");
|
||||||
|
|
||||||
|
ServiceManager.Log.Verbose("PluginManager MUST depend on {Type} via {BaseType}", scopedDep.FullName!, serviceType.FullName!);
|
||||||
|
res.Add(scopedDep);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginInterfaceAttribute = serviceType.GetCustomAttribute<PluginInterfaceAttribute>(true);
|
||||||
|
if (pluginInterfaceAttribute == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ServiceManager.Log.Verbose("PluginManager MUST depend on {Type}", serviceType.FullName!);
|
ServiceManager.Log.Verbose("PluginManager MUST depend on {Type}", serviceType.FullName!);
|
||||||
|
|
@ -164,7 +184,6 @@ internal static class Service<T> where T : IServiceType
|
||||||
|
|
||||||
return res
|
return res
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Select(x => typeof(Service<>).MakeGenericType(x))
|
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,3 +314,37 @@ internal static class Service<T> where T : IServiceType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper functions for services.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ServiceHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get a list of dependencies for a service. Only accepts Service<T> types.
|
||||||
|
/// These are returned as Service<T> types.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceType">The dependencies for this service.</param>
|
||||||
|
/// <returns>A list of dependencies.</returns>
|
||||||
|
public static List<Type> GetDependencies(Type serviceType)
|
||||||
|
{
|
||||||
|
return (List<Type>)serviceType.InvokeMember(
|
||||||
|
"GetDependencyServices",
|
||||||
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null) ?? new List<Type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Service<T> type for a given service type.
|
||||||
|
/// This will throw if the service type is not a valid service.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type to obtain a Service<T> for.</param>
|
||||||
|
/// <returns>The Service<T>.</returns>
|
||||||
|
public static Type GetAsService(Type type)
|
||||||
|
{
|
||||||
|
return typeof(Service<>)
|
||||||
|
.MakeGenericType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue