- Added ISystem (automatically run services) service type.

This commit is contained in:
MapleWheels
2026-02-10 14:28:22 -05:00
parent 30149b504d
commit 6b9e48f96a
4 changed files with 74 additions and 31 deletions
@@ -215,7 +215,7 @@ namespace Barotrauma
servicesProvider.RegisterServiceType<IPackageManagementServiceConfig, PackageManagementServiceConfig>(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType<IPackageManagementServiceConfig, PackageManagementServiceConfig>(ServiceLifetime.Singleton);
// gen IL // gen IL
servicesProvider.Compile(); servicesProvider.CompileAndRun();
return servicesProvider; return servicesProvider;
} }
@@ -1,5 +1,8 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -12,7 +15,14 @@ public class ServicesProvider : IServicesProvider
{ {
private ServiceContainer _serviceContainerInst; private ServiceContainer _serviceContainerInst;
private ServiceContainer ServiceContainer => _serviceContainerInst; private ServiceContainer ServiceContainer => _serviceContainerInst;
/// <summary>
/// Definition: [Key: InterfaceType, Value: ConcreteTypes]
/// </summary>
private readonly ConcurrentDictionary<Type, ConcurrentBag<Type>> _systemTypeDefs = new();
/// <summary>
/// Definition: [Key: ConcreteType, Value: TypeInstance]
/// </summary>
private readonly ConcurrentDictionary<Type, ISystem> _systemInstances = new();
private readonly ReaderWriterLockSlim _serviceLock = new(); private readonly ReaderWriterLockSlim _serviceLock = new();
public ServicesProvider() public ServicesProvider()
@@ -27,6 +37,13 @@ public class ServicesProvider : IServicesProvider
public void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface public void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface
{ {
// ISystem services must run as a lifetime singleton
if (typeof(TSvcInterface).IsAssignableTo(typeof(ISystem)))
{
lifetimeInstance = new PerContainerLifetime();
_systemTypeDefs.GetOrAdd(typeof(TSvcInterface), (type) => new ConcurrentBag<Type>());
}
if (lifetimeInstance is null) if (lifetimeInstance is null)
{ {
switch (lifetime) switch (lifetime)
@@ -54,7 +71,6 @@ public class ServicesProvider : IServicesProvider
ServiceContainer.Register<TSvcInterface, TService>(lifetimeInstance); ServiceContainer.Register<TSvcInterface, TService>(lifetimeInstance);
else else
ServiceContainer.Register<TSvcInterface, TService>(); ServiceContainer.Register<TSvcInterface, TService>();
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
} }
finally finally
{ {
@@ -70,6 +86,13 @@ public class ServicesProvider : IServicesProvider
throw new ArgumentNullException($"Tried to register a service of type {typeof(TService).Name} but the name provided is null or empty." ); throw new ArgumentNullException($"Tried to register a service of type {typeof(TService).Name} but the name provided is null or empty." );
} }
// ISystem services must run as a lifetime singleton
if (typeof(TSvcInterface).IsAssignableTo(typeof(ISystem)))
{
lifetimeInstance = new PerContainerLifetime();
_systemTypeDefs.GetOrAdd(typeof(TSvcInterface), (type) => new ConcurrentBag<Type>());
}
if (lifetimeInstance is null) if (lifetimeInstance is null)
{ {
switch (lifetime) switch (lifetime)
@@ -94,7 +117,6 @@ public class ServicesProvider : IServicesProvider
{ {
_serviceLock.EnterReadLock(); _serviceLock.EnterReadLock();
ServiceContainer.Register<TSvcInterface, TService>(name, lifetimeInstance); ServiceContainer.Register<TSvcInterface, TService>(name, lifetimeInstance);
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
} }
finally finally
{ {
@@ -115,21 +137,27 @@ public class ServicesProvider : IServicesProvider
} }
} }
public void Compile() public void CompileAndRun()
{ {
try try
{ {
_serviceLock.EnterReadLock(); _serviceLock.EnterWriteLock();
ServiceContainer?.Compile(); ServiceContainer?.Compile();
foreach (var typeDef in _systemTypeDefs.Values.SelectMany(type => type))
{
if (_systemInstances.ContainsKey(typeDef))
{
continue;
}
_systemInstances[typeDef] = (ISystem)ServiceContainer?.TryGetInstance(typeDef);
}
} }
finally finally
{ {
_serviceLock.ExitReadLock(); _serviceLock.ExitWriteLock();
} }
} }
public event Action<Type, Type> OnServiceRegistered;
public void InjectServices<T>(T inst) where T : class public void InjectServices<T>(T inst) where T : class
{ {
try try
@@ -209,19 +237,32 @@ public class ServicesProvider : IServicesProvider
} }
} }
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.Synchronized)]
public void DisposeAndReset() public void DisposeAndReset()
{ {
// Plugins should never be allowed to execute this. // Plugins should never be allowed to execute this.
if (Assembly.GetCallingAssembly() != Assembly.GetExecutingAssembly()) if (Assembly.GetCallingAssembly() != Assembly.GetExecutingAssembly())
{ {
throw new MethodAccessException( throw new MethodAccessException(
$"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call DisposeAllServices()."); $"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call {nameof(DisposeAndReset)}().");
} }
try try
{ {
_serviceLock.EnterWriteLock(); _serviceLock.EnterWriteLock();
foreach (var system in _systemInstances.Values)
{
try
{
system.Dispose();
}
catch (Exception e)
{
// ignored, no logging services available.
}
}
_systemInstances.Clear();
_systemTypeDefs.Clear();
_serviceContainerInst?.Dispose(); _serviceContainerInst?.Dispose();
_serviceContainerInst = new ServiceContainer(); _serviceContainerInst = new ServiceContainer();
} }
@@ -3,6 +3,12 @@ using Microsoft.Toolkit.Diagnostics;
namespace Barotrauma.LuaCs; namespace Barotrauma.LuaCs;
/// <summary>
/// Represents a <see cref="IReusableService"/> that is automatically instantiated at startup for the lifetime of the
/// <see cref="IServiceProvider"/> instance.
/// </summary>
public interface ISystem : IReusableService { }
/// <summary> /// <summary>
/// Defines a service that can be reset to it's post-constructor state and reused without needing to be disposed. /// Defines a service that can be reset to it's post-constructor state and reused without needing to be disposed.
/// Intended for persistent services. /// Intended for persistent services.
@@ -6,7 +6,8 @@ using LightInject;
namespace Barotrauma.LuaCs; namespace Barotrauma.LuaCs;
/// <summary> /// <summary>
/// Provides instancing and management of IServices. /// Provides instancing and management of <see cref="IService"/>, <see cref="IReusableService"/>, and <see cref="ISystem"/>
/// instances.
/// </summary> /// </summary>
public interface IServicesProvider public interface IServicesProvider
{ {
@@ -15,29 +16,24 @@ public interface IServicesProvider
/// <summary> /// <summary>
/// Registers a type as a service for a given interface. /// Registers a type as a service for a given interface.
/// </summary> /// </summary>
/// <param name="lifetime"></param> /// <remarks>NOTE: <see cref="ISystem"/> services are forced to <see cref="ServiceLifetime.Singleton"/></remarks>
/// <param name="lifetimeInstance"></param> /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service when requested.</param>
/// <typeparam name="TSvcInterface"></typeparam> /// <param name="lifetimeInstance">Custom lifetime instance.</param>
/// <typeparam name="TService"></typeparam> /// <typeparam name="TSvcInterface">Service interface.</typeparam>
/// <typeparam name="TService">Implementing service type.</typeparam>
void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface; void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface;
/// <summary> /// <summary>
/// Registers a type as a service for a given interface that can be requested by name. /// Registers a type as a service for a given interface that can be requested by name.
/// </summary> /// </summary>
/// <param name="name"></param> /// <remarks>NOTE: <see cref="ISystem"/> services are forced to <see cref="ServiceLifetime.Singleton"/></remarks>
/// <param name="lifetime"></param> /// <param name="name">Name of the service for lookup.</param>
/// <param name="lifetimeInstance"></param> /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service when requested.</param>
/// <typeparam name="TSvcInterface"></typeparam> /// <param name="lifetimeInstance">Custom lifetime instance.</param>
/// <typeparam name="TService"></typeparam> /// <typeparam name="TSvcInterface">Service interface.</typeparam>
/// <typeparam name="TService">Implementing service type.</typeparam>
void RegisterServiceType<TSvcInterface, TService>(string name, ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface; void RegisterServiceType<TSvcInterface, TService>(string name, ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface;
/// <summary>
/// Called whenever a new service type for a given interface is implemented.
/// Args[0]: Interface type
/// Args[1]: Implementing type
/// </summary>
event System.Action<Type, Type> OnServiceRegistered;
/// <summary> /// <summary>
/// Registers a factory for resolving the service type. /// Registers a factory for resolving the service type.
/// </summary> /// </summary>
@@ -46,9 +42,9 @@ public interface IServicesProvider
void RegisterServiceResolver<TSvcInterface>(Func<ServiceContainer, TSvcInterface> factory) where TSvcInterface : class, IService; void RegisterServiceResolver<TSvcInterface>(Func<ServiceContainer, TSvcInterface> factory) where TSvcInterface : class, IService;
/// <summary> /// <summary>
/// Runs compilation of registered services. /// Compiles/Generates IL for registered services and instantiates all registered <see cref="ISystem"/> types.
/// </summary> /// </summary>
public void Compile(); public void CompileAndRun();
#endregion #endregion