feat: enable early loaded services to wait for provided services, some rewrites to make service kind declaration more explicit

This commit is contained in:
goat 2023-10-01 21:12:27 +02:00
parent 0690f5dd2a
commit 2bdb837577
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
4 changed files with 37 additions and 26 deletions

View file

@ -74,14 +74,17 @@ internal sealed class Dalamud : IServiceType
if (!configuration.IsResumeGameAfterPluginLoad) if (!configuration.IsResumeGameAfterPluginLoad)
{ {
NativeFunctions.SetEvent(mainThreadContinueEvent); NativeFunctions.SetEvent(mainThreadContinueEvent);
try ServiceManager.InitializeEarlyLoadableServices()
{ .ContinueWith(t =>
_ = ServiceManager.InitializeEarlyLoadableServices(); {
} if (t.IsCompletedSuccessfully)
catch (Exception e) return;
{
Log.Error(e, "Service initialization failure"); Log.Error(t.Exception!, "Service initialization failure");
} Util.Fatal(
"Dalamud failed to load all necessary services.\n\nThe game will continue, but you may not be able to use plugins.",
"Dalamud", false);
});
} }
else else
{ {

View file

@ -1276,6 +1276,7 @@ internal class InterfaceManager : IDisposable, IServiceType
/// <summary> /// <summary>
/// Represents an instance of InstanceManager with scene ready for use. /// Represents an instance of InstanceManager with scene ready for use.
/// </summary> /// </summary>
[ServiceManager.Service]
public class InterfaceManagerWithScene : IServiceType public class InterfaceManagerWithScene : IServiceType
{ {
/// <summary> /// <summary>

View file

@ -47,9 +47,9 @@ internal static class ServiceManager
None = 0, None = 0,
/// <summary> /// <summary>
/// Regular service. /// Service that is loaded manually.
/// </summary> /// </summary>
ManualService = 1 << 0, ProvidedService = 1 << 0,
/// <summary> /// <summary>
/// Service that is loaded asynchronously while the game starts. /// Service that is loaded asynchronously while the game starts.
@ -90,7 +90,7 @@ internal static class ServiceManager
{ {
void ProvideService<T>(T service) where T : IServiceType void ProvideService<T>(T service) where T : IServiceType
{ {
Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ManualService), "Provided service must have Service attribute"); Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ProvidedService), "Provided service must have Service attribute");
Service<T>.Provide(service); Service<T>.Provide(service);
LoadedServices.Add(typeof(T)); LoadedServices.Add(typeof(T));
} }
@ -119,23 +119,18 @@ internal static class ServiceManager
var serviceContainer = Service<ServiceContainer>.Get(); var serviceContainer = Service<ServiceContainer>.Get();
foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes()) foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsAssignableTo(typeof(IServiceType)) && !x.IsInterface && !x.IsAbstract))
{ {
var serviceKind = serviceType.GetServiceKind(); var serviceKind = serviceType.GetServiceKind();
if (serviceKind is ServiceKind.None) if (serviceKind is ServiceKind.None)
continue; throw new Exception($"Service<{serviceType.FullName}> did not specify a kind");
// Let IoC know about the interfaces this service implements // Let IoC know about the interfaces this service implements
serviceContainer.RegisterInterfaces(serviceType); serviceContainer.RegisterInterfaces(serviceType);
// Scoped service do not go through Service<T> and are never early loaded // Scoped service do not go through Service<T> and are never early loaded
// Manual services are provided if (serviceKind.HasFlag(ServiceKind.ScopedService))
if (serviceKind.HasFlag(ServiceKind.ScopedService) || serviceKind.HasFlag(ServiceKind.ManualService))
continue; continue;
Debug.Assert(
!serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService),
"Regular and scoped services should never be loaded early");
var genericWrappedServiceType = typeof(Service<>).MakeGenericType(serviceType); var genericWrappedServiceType = typeof(Service<>).MakeGenericType(serviceType);
@ -147,9 +142,14 @@ internal static class ServiceManager
null, null,
null); null);
getAsyncTaskMap[serviceType] = getTask;
// We don't actually need to load provided services, something else does
if (serviceKind.HasFlag(ServiceKind.ProvidedService))
continue;
if (serviceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService)) if (serviceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService))
{ {
getAsyncTaskMap[serviceType] = getTask;
blockingEarlyLoadingServices.Add(serviceType); blockingEarlyLoadingServices.Add(serviceType);
} }
else else
@ -191,13 +191,13 @@ internal static class ServiceManager
var hasDeps = true; var hasDeps = true;
foreach (var dependency in dependencyServicesMap[serviceType]) foreach (var dependency in dependencyServicesMap[serviceType])
{ {
var depServiceKind = dependency.GetServiceKind(); var depUnderlyingServiceType = dependency.GetGenericArguments().First();
var depResolveTask = getAsyncTaskMap.GetValueOrDefault(dependency); var depResolveTask = getAsyncTaskMap.GetValueOrDefault(depUnderlyingServiceType);
if (depResolveTask == null && (depServiceKind.HasFlag(ServiceKind.EarlyLoadedService) || depServiceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService))) if (depResolveTask == null)
{ {
Log.Error("{Type}: {Dependency} has no resolver task, is it early loaded or blocking early loaded?", serviceType.FullName!, dependency.FullName!); Log.Error("{Type}: {Dependency} has no resolver task", serviceType.FullName!, dependency.FullName!);
Debug.Assert(false, $"No resolver for dependent service {dependency.FullName}"); Debug.Assert(false, $"No resolver for dependent service {depUnderlyingServiceType.FullName}");
} }
else if (depResolveTask is { IsCompleted: false }) else if (depResolveTask is { IsCompleted: false })
{ {
@ -386,7 +386,7 @@ internal static class ServiceManager
if (attr.IsAssignableTo(typeof(ScopedService))) if (attr.IsAssignableTo(typeof(ScopedService)))
return ServiceKind.ScopedService; return ServiceKind.ScopedService;
return ServiceKind.ManualService; return ServiceKind.ProvidedService;
} }
/// <summary> /// <summary>

View file

@ -123,6 +123,8 @@ internal static class Service<T> where T : IServiceType
public static List<Type> GetDependencyServices() public static List<Type> GetDependencyServices()
{ {
var res = new List<Type>(); var res = new List<Type>();
ServiceManager.Log.Verbose("Service<{0}>: Getting dependencies", typeof(T).Name);
var ctor = GetServiceConstructor(); var ctor = GetServiceConstructor();
if (ctor != null) if (ctor != null)
@ -181,6 +183,11 @@ internal static class Service<T> where T : IServiceType
res.Add(serviceType); res.Add(serviceType);
} }
} }
foreach (var type in res)
{
ServiceManager.Log.Verbose("Service<{0}>: => Dependency: {1}", typeof(T).Name, type.Name);
}
return res return res
.Distinct() .Distinct()