- Added ISystem (automatically run services) service type.
This commit is contained in:
@@ -215,7 +215,7 @@ namespace Barotrauma
|
||||
servicesProvider.RegisterServiceType<IPackageManagementServiceConfig, PackageManagementServiceConfig>(ServiceLifetime.Singleton);
|
||||
|
||||
// gen IL
|
||||
servicesProvider.Compile();
|
||||
servicesProvider.CompileAndRun();
|
||||
return servicesProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@@ -12,7 +15,14 @@ public class ServicesProvider : IServicesProvider
|
||||
{
|
||||
private 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();
|
||||
|
||||
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
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
switch (lifetime)
|
||||
@@ -54,7 +71,6 @@ public class ServicesProvider : IServicesProvider
|
||||
ServiceContainer.Register<TSvcInterface, TService>(lifetimeInstance);
|
||||
else
|
||||
ServiceContainer.Register<TSvcInterface, TService>();
|
||||
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
|
||||
}
|
||||
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." );
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
switch (lifetime)
|
||||
@@ -94,7 +117,6 @@ public class ServicesProvider : IServicesProvider
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
ServiceContainer.Register<TSvcInterface, TService>(name, lifetimeInstance);
|
||||
OnServiceRegistered?.Invoke(typeof(TSvcInterface), typeof(TService));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -115,20 +137,26 @@ public class ServicesProvider : IServicesProvider
|
||||
}
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
public void CompileAndRun()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceLock.EnterReadLock();
|
||||
_serviceLock.EnterWriteLock();
|
||||
ServiceContainer?.Compile();
|
||||
foreach (var typeDef in _systemTypeDefs.Values.SelectMany(type => type))
|
||||
{
|
||||
if (_systemInstances.ContainsKey(typeDef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_systemInstances[typeDef] = (ISystem)ServiceContainer?.TryGetInstance(typeDef);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serviceLock.ExitReadLock();
|
||||
_serviceLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<Type, Type> OnServiceRegistered;
|
||||
|
||||
public void InjectServices<T>(T inst) where T : class
|
||||
{
|
||||
@@ -209,19 +237,32 @@ public class ServicesProvider : IServicesProvider
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.NoInlining)]
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.Synchronized)]
|
||||
public void DisposeAndReset()
|
||||
{
|
||||
// Plugins should never be allowed to execute this.
|
||||
if (Assembly.GetCallingAssembly() != Assembly.GetExecutingAssembly())
|
||||
{
|
||||
throw new MethodAccessException(
|
||||
$"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call DisposeAllServices().");
|
||||
$"Assembly {Assembly.GetCallingAssembly().FullName} attempted to call {nameof(DisposeAndReset)}().");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_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 = new ServiceContainer();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@ using Microsoft.Toolkit.Diagnostics;
|
||||
|
||||
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>
|
||||
/// Defines a service that can be reset to it's post-constructor state and reused without needing to be disposed.
|
||||
/// Intended for persistent services.
|
||||
|
||||
@@ -6,7 +6,8 @@ using LightInject;
|
||||
namespace Barotrauma.LuaCs;
|
||||
|
||||
/// <summary>
|
||||
/// Provides instancing and management of IServices.
|
||||
/// Provides instancing and management of <see cref="IService"/>, <see cref="IReusableService"/>, and <see cref="ISystem"/>
|
||||
/// instances.
|
||||
/// </summary>
|
||||
public interface IServicesProvider
|
||||
{
|
||||
@@ -15,28 +16,23 @@ public interface IServicesProvider
|
||||
/// <summary>
|
||||
/// Registers a type as a service for a given interface.
|
||||
/// </summary>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="lifetimeInstance"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <remarks>NOTE: <see cref="ISystem"/> services are forced to <see cref="ServiceLifetime.Singleton"/></remarks>
|
||||
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service when requested.</param>
|
||||
/// <param name="lifetimeInstance">Custom lifetime instance.</param>
|
||||
/// <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;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a type as a service for a given interface that can be requested by name.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="lifetimeInstance"></param>
|
||||
/// <typeparam name="TSvcInterface"></typeparam>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <remarks>NOTE: <see cref="ISystem"/> services are forced to <see cref="ServiceLifetime.Singleton"/></remarks>
|
||||
/// <param name="name">Name of the service for lookup.</param>
|
||||
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service when requested.</param>
|
||||
/// <param name="lifetimeInstance">Custom lifetime instance.</param>
|
||||
/// <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;
|
||||
|
||||
/// <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>
|
||||
/// Registers a factory for resolving the service type.
|
||||
@@ -46,14 +42,14 @@ public interface IServicesProvider
|
||||
void RegisterServiceResolver<TSvcInterface>(Func<ServiceContainer, TSvcInterface> factory) where TSvcInterface : class, IService;
|
||||
|
||||
/// <summary>
|
||||
/// Runs compilation of registered services.
|
||||
/// Compiles/Generates IL for registered services and instantiates all registered <see cref="ISystem"/> types.
|
||||
/// </summary>
|
||||
public void Compile();
|
||||
public void CompileAndRun();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Services_Instancing_Injection
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Injects services into the properties of already instanced objects.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user