Skip to content
This repository has been archived by the owner on Feb 14, 2022. It is now read-only.

Commit

Permalink
Added instances limit
Browse files Browse the repository at this point in the history
- remove check for self injection
- add InstancesLimitReachedException
- add cloned param to ServeAll
- add sealed modifier to exceptions
- add total_count and CountAllInstances to InstancesHolder
- modified tests
  • Loading branch information
JacopoWolf committed Jan 16, 2021
1 parent 240a376 commit 5dd3e45
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 38 deletions.
4 changes: 2 additions & 2 deletions StackInjector/Core/Cloning/ClonedCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public IAsyncStackWrapper<TEntry, TIn, TOut> ToAsyncWrapper<TEntry, TIn, TOut> (
this.clonedCore.EntryType = typeof(TEntry);
if( this.clonedCore.settings._registerAfterCloning )
this.clonedCore.ReadAssemblies();
this.clonedCore.ServeAll();
this.clonedCore.ServeAll(cloned:true);

return wrapper;
}
Expand All @@ -34,7 +34,7 @@ public IStackWrapper<T> ToWrapper<T> ()
this.clonedCore.EntryType = typeof(T);
if( this.clonedCore.settings._registerAfterCloning )
this.clonedCore.ReadAssemblies();
this.clonedCore.ServeAll();
this.clonedCore.ServeAll(cloned:true);

return wrapper;
}
Expand Down
18 changes: 1 addition & 17 deletions StackInjector/Core/injectionCore/InjectionCore.injection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ private void InjectFields ( Type type, object instance, ref List<object> used, b
var serviceInstance =
this.InstTypeOrServiceEnum
(
type,
serviceField.FieldType,
serviceField.GetCustomAttribute<ServedAttribute>(),
ref used
Expand Down Expand Up @@ -92,7 +91,6 @@ private void InjectProperties ( Type type, object instance, ref List<object> use
var serviceInstance =
this.InstTypeOrServiceEnum
(
type,
serviceProperty.PropertyType,
serviceProperty.GetCustomAttribute<ServedAttribute>(),
ref used
Expand All @@ -104,7 +102,7 @@ ref used


// returns the instantiated object
private object InstTypeOrServiceEnum ( Type host, Type type, ServedAttribute servedAttribute, ref List<object> used )
private object InstTypeOrServiceEnum ( Type type, ServedAttribute servedAttribute, ref List<object> used )
{
if (
this.settings._serveEnumerables
Expand All @@ -125,7 +123,6 @@ private object InstTypeOrServiceEnum ( Type host, Type type, ServedAttribute ser
// gather instances if necessary
foreach( var serviceType in validTypes )
{
SelfInjectionCheck(host, serviceType);
var obj = this.OfTypeOrInstantiate(serviceType);
instances.Add(obj);
used.Add(obj);
Expand All @@ -135,26 +132,13 @@ private object InstTypeOrServiceEnum ( Type host, Type type, ServedAttribute ser
else
{
var serviceType = this.ClassOrVersionFromInterface( type, servedAttribute );
SelfInjectionCheck(host, serviceType);

var obj = this.OfTypeOrInstantiate(serviceType);
used.Add(obj);
return obj;
}
}

// static check to avaid self injection infinite loops
private static void SelfInjectionCheck ( Type host, Type child )
{
if( child == host || child.IsSubclassOf(host) )
throw new StackInjectorException(
host,
$"{child.Name} is the same type or inherits from {host.Name} " +
$"and therefore cannot be served.",
new InvalidOperationException()
);
}

#endregion

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private object InstantiateService ( Type type )


this.instances[type].AddLast(instance);
this.instances.total_count++;

// if true, track instantiated objects
if( this.settings._trackInstancesDiff )
Expand Down
26 changes: 24 additions & 2 deletions StackInjector/Core/injectionCore/InjectionCore.logic.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using System.Collections.Generic;
using System.Linq;
using StackInjector.Exceptions;

namespace StackInjector.Core
{
internal partial class InjectionCore
{

internal void ServeAll ()
internal void ServeAll ( bool cloned = false )
{
// those don't need to be inside the lock.
var injected = new HashSet<object>();
var toInject = new Queue<object>();

if ( cloned )
this.instances.CountAllInstances();


// ensures that two threads are not trying to Dispose/InjectAll at the same time
lock( this._lock )
Expand All @@ -22,6 +26,9 @@ internal void ServeAll ()
// instantiates and enqueues the EntryPoint
toInject.Enqueue(this.OfTypeOrInstantiate(this.EntryType));

checkInstancesLimit();


// enqueuing loop
while( toInject.Any() )
{
Expand All @@ -34,13 +41,28 @@ internal void ServeAll ()
// foreach injected object check if it has already been injected.
// saves time in most situations
foreach( var service in usedServices )
if( !injected.Contains(service) )
{
if ( !injected.Contains(service) )
{
toInject.Enqueue(service);
checkInstancesLimit();
}
}
}

// cleanup
if( this.settings._cleanUnusedTypesAftInj )
this.RemoveUnusedTypes();



void checkInstancesLimit ()
{
if ( this.instances.total_count > this.settings._limitInstancesCount )
throw new InstancesLimitReachedException(
$"Reached limit of {this.settings._limitInstancesCount} instances."
);
}
}

}
Expand Down
11 changes: 10 additions & 1 deletion StackInjector/Core/injectionCore/InstancesHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace StackInjector.Core
{
internal class InstancesHolder : Dictionary<Type, LinkedList<object>>
{
internal uint total_count = 0;

internal IEnumerable<Type> TypesAssignableFrom ( Type type )
{
Expand All @@ -26,6 +27,15 @@ internal void AddType ( Type type )
this.TryAdd(type, new LinkedList<object>());
}


internal void CountAllInstances ()
{
this.total_count = 0;
foreach (var pair in this)
this.total_count += (uint)pair.Value.Count;
}


// clones just the structure, the classes references are not cloned
internal InstancesHolder CloneStructure ()
{
Expand All @@ -37,6 +47,5 @@ internal InstancesHolder CloneStructure ()
return clonedHolder;

}

}
}
23 changes: 23 additions & 0 deletions StackInjector/Exceptions/InstancesLimitReachedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace StackInjector.Exceptions
{

/// <summary>
/// Thrown when the number of instances is greater than the one in the settings/>
/// </summary>
public sealed class InstancesLimitReachedException : StackInjectorException
{
internal InstancesLimitReachedException ()
{
}

internal InstancesLimitReachedException ( string message ) : base(message)
{
}

internal InstancesLimitReachedException ( string message, Exception innerException ) : base(message, innerException)
{
}
}
}
2 changes: 1 addition & 1 deletion StackInjector/Exceptions/InvalidEntryTypeException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace StackInjector.Exceptions
/// <summary>
/// Thrown when the entry point is invalid.
/// </summary>
public class InvalidEntryTypeException : StackInjectorException
public sealed class InvalidEntryTypeException : StackInjectorException
{
internal InvalidEntryTypeException () { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace StackInjector.Exceptions
/// <summary>
/// Thrown when a class has no parameterless constructor to call.
/// </summary>
public class MissingParameterlessConstructorException : StackInjectorException
public sealed class MissingParameterlessConstructorException : StackInjectorException
{
internal MissingParameterlessConstructorException () { }

Expand Down
2 changes: 1 addition & 1 deletion StackInjector/Exceptions/NoSetterException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace StackInjector.Exceptions
/// <summary>
/// Thrown when the specified type has a field/property withouth a setter
/// </summary>
public class NoSetterException : StackInjectorException
public sealed class NoSetterException : StackInjectorException
{
internal NoSetterException () { }

Expand Down
2 changes: 1 addition & 1 deletion StackInjector/Exceptions/NotAServiceException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace StackInjector.Exceptions
/// <summary>
/// Thrown when the specified class is NOT a marked with <see cref="Attributes.ServiceAttribute"/>.
/// </summary>
public class NotAServiceException : StackInjectorException
public sealed class NotAServiceException : StackInjectorException
{
internal NotAServiceException () { }

Expand Down
12 changes: 12 additions & 0 deletions StackInjector/Settings/StackWrapperSettings.configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ public StackWrapperSettings RemoveUnusedTypesAfterInjection ( bool remove = true
return this;
}

/// <summary>
/// Limits the TOTAL number of instances. <br/>
/// You can use <see cref="uint.MaxValue"/> to remove this limit, altough it is suggest to use the lowest possible value.
/// </summary>
/// <param name="count">the limit of total instances</param>
/// <returns>The modified settings</returns>
public StackWrapperSettings LimitInstancesCount ( uint count = 128 )
{
this._limitInstancesCount = count;
return this;
}

#endregion


Expand Down
1 change: 1 addition & 0 deletions StackInjector/Settings/StackWrapperSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public sealed partial class StackWrapperSettings
internal ServingMethods _servingMethod = ServingMethods.DoNotServe;
internal bool _overrideServingMethod;
internal bool _cleanUnusedTypesAftInj;
internal uint _limitInstancesCount = 128;

// features
internal bool _serveEnumerables;
Expand Down
34 changes: 22 additions & 12 deletions tests/StackInjector.TEST.BlackBox/UseCases/Exceptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using NUnit.Framework;
using StackInjector.Attributes;
using StackInjector.Exceptions;
Expand Down Expand Up @@ -113,13 +114,9 @@ private class InstantiationPatternBase {[Served] private readonly AlwaysCreateLo

[Test]
[Timeout(500)]
public void ThrowsExceptionOnInjectionLoop_AlwaysCreate ()
public void ThrowsInstLimitReach_AlwaysCreate ()
{
Assert.Multiple(() =>
{
var ex = Assert.Throws<StackInjectorException>(() => Injector.From<InstantiationPatternBase>());
Assert.That(ex.InnerException, Is.TypeOf<InvalidOperationException>());
});
Assert.Throws<InstancesLimitReachedException>(() => Injector.From<InstantiationPatternBase>());
}


Expand All @@ -135,13 +132,26 @@ private class LoopInjectionThrower

[Test]
[Timeout(500)]
public void ThrowsExceptionOnInjectionLoop ()
public void NotThrowsInstLimitReach_Singleton ()
{
Assert.Multiple(() =>
{
var ex = Assert.Throws<StackInjectorException>(() => Injector.From<LoopInjectionThrower>());
Assert.That(ex.InnerException, Is.TypeOf<InvalidOperationException>());
});
var settings = StackWrapperSettings.Default
.LimitInstancesCount(1);
Assert.DoesNotThrow(() => Injector.From<LoopInjectionThrower>(settings));
}


// ----------




[Test]
[Timeout(500)]
public void ThrowsInstLimitReach_Cloned ()
{
var settings = StackWrapperSettings.Default
.LimitInstancesCount(1);
Assert.Throws<InstancesLimitReachedException>( () => Injector.From<IBase>().CloneCore(settings).ToWrapper<IBase>() );
}

}
Expand Down

0 comments on commit 5dd3e45

Please sign in to comment.