Skip to content
This repository was archived by the owner on Oct 20, 2023. It is now read-only.

Commit 793a222

Browse files
authored
Merge pull request #79 from Barsonax/feeature/scopeoptimizations
Scopeoptimizations
2 parents e5a2657 + 9635deb commit 793a222

File tree

8 files changed

+199
-43
lines changed

8 files changed

+199
-43
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ src/Duality/UserData.dat
276276
src/Duality/DesignTimeData.dat
277277
src/Duality/DefaultUserData.dat
278278
src/Duality/AppData.dat
279+
src/Docs/repository
279280
src/Duality/Plugins/Singularity.Duality.Examples.core.dll
280281
/coverage
281282
/BuildOutput

README.md

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
# Singularity
22
[![Discord](https://img.shields.io/discord/569232642105540608.svg)](https://discord.gg/9x9J3y) [![NuGet Badge](https://buildstats.info/nuget/Singularity)](https://www.nuget.org/packages/Singularity/) [![Build Status](https://dev.azure.com/Barsonax/Singularity/_apis/build/status/Singularity-CI?branchName=master)](https://dev.azure.com/Barsonax/Singularity/_build/latest?definitionId=7&branchName=master) ![Azure DevOps tests (branch)](https://img.shields.io/azure-devops/tests/Barsonax/Singularity/7/master.svg) ![coverage](https://img.shields.io/azure-devops/coverage/Barsonax/Singularity/7/master.svg) [![Beerpay](https://img.shields.io/beerpay/Barsonax/Singularity.svg)](https://beerpay.io/Barsonax/Singularity)
33

4-
Singularity is a ioc container that focuses on the following things
5-
- Performance, Singularity is one of the fastest if not the fastest dependency injection container out there. Don't believe me? Check out my [benchmarks](#Benchmarks) or if you want a second opinion check out the benchmarks that Daniel Palme made [here](https://github.com/danielpalme/IocPerformance).
6-
- Not invasive. For instance `Dispose` wont be automatically called by default, instead you can configure Singularity to do so through the `With(DisposeBehavior)` method or tell singularity to automatically call `Dispose` by simply changing the settings. Is calling Dispose not enough and you want to invoke your own custom logic? The `WithFinalizer(Action<TInstance>)` method might be of help here.
4+
## Features
5+
- Extreme performance, Singularity is one of the fastest if not the fastest dependency injection container out there. Don't believe me? Check out my [benchmarks](#Benchmarks) or if you want a second opinion check out the benchmarks that Daniel Palme made [here](https://github.com/danielpalme/IocPerformance).
6+
- Clean fluent API.
7+
- Generic wrappers:
8+
1. `Func<T>`
9+
1. `Lazy<T>`
10+
1. `Expression<Func<T>>`
11+
- Collection support:
12+
1. `IEnumerable<T>`
13+
1. `IReadOnlyCollection<T>`
14+
1. `IReadOnlyList`
15+
- Supports open generics.
16+
- Supports resolving unregistered concrete types.
17+
- Supports decorators.
18+
- Supports method and property injection without forcing you to litter attributes all over your code base. All configuration is kept inside the container.
19+
- Auto dispose, this is off by default but can be turned on with `With(DisposeBehavior)`or by setting `SingularitySettings.AutoDispose` to true.
20+
- Custom finalizers with the `WithFinalizer(Action<TInstance>)` method.
21+
- Supports Transient, Singleton and Scope lifetimes.
22+
- Supports child containers.
723
- Clear error messages and fail fast to point you in the right direction as fast as possible.
824

925
## Getting started

src/Docs/repository

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Runtime.CompilerServices;
5+
6+
namespace Singularity.Collections
7+
{
8+
/// <summary>
9+
/// A immutable singly linked list.
10+
/// Note that adding items to this list will reverse the order.
11+
/// </summary>
12+
/// <typeparam name="TValue"></typeparam>
13+
public sealed class SinglyLinkedListKeyNode<TKey, TValue> : IEnumerable<TValue>
14+
{
15+
public readonly TKey Key;
16+
17+
/// <summary>
18+
/// The value of this node.
19+
/// </summary>
20+
public readonly TValue Value;
21+
22+
/// <summary>
23+
/// The next node in the list.
24+
/// </summary>
25+
public readonly SinglyLinkedListKeyNode<TKey, TValue>? Next;
26+
27+
/// <summary>
28+
/// Creates a new list with the provided value.
29+
/// </summary>
30+
/// <param name="value"></param>
31+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32+
public SinglyLinkedListKeyNode(in TKey key, in TValue value)
33+
{
34+
Key = key;
35+
Value = value;
36+
}
37+
38+
/// <summary>
39+
/// Adds a new node to a existing list with the provided value.
40+
/// </summary>
41+
/// <param name="next"></param>
42+
/// <param name="value"></param>
43+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
44+
public SinglyLinkedListKeyNode(SinglyLinkedListKeyNode<TKey, TValue>? next, in TKey key, in TValue value)
45+
{
46+
Next = next;
47+
Key = key;
48+
Value = value;
49+
}
50+
51+
/// <summary>
52+
/// Uses a more efficient enumerator when the type is known.
53+
/// </summary>
54+
/// <returns></returns>
55+
public Enumerator GetEnumerator()
56+
{
57+
return new Enumerator(this);
58+
}
59+
60+
IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
61+
{
62+
return GetEnumerator();
63+
}
64+
65+
IEnumerator IEnumerable.GetEnumerator()
66+
{
67+
return GetEnumerator();
68+
}
69+
70+
/// <summary>
71+
/// Efficient enumerator.
72+
/// </summary>
73+
/// <returns></returns>
74+
public sealed class Enumerator : IEnumerator<TValue>
75+
{
76+
private SinglyLinkedListKeyNode<TKey, TValue>? _node;
77+
78+
/// <summary>
79+
/// The value of the current node.
80+
/// </summary>
81+
public TValue Current { get; private set; }
82+
83+
object IEnumerator.Current => Current!;
84+
85+
internal Enumerator(SinglyLinkedListKeyNode<TKey, TValue> list)
86+
{
87+
_node = list;
88+
Current = default!;
89+
}
90+
91+
/// <inheritdoc />
92+
public bool MoveNext()
93+
{
94+
if (_node != null)
95+
{
96+
Current = _node.Value;
97+
_node = _node.Next;
98+
return true;
99+
}
100+
return false;
101+
}
102+
103+
/// <inheritdoc />
104+
public void Reset()
105+
{
106+
throw new NotImplementedException();
107+
}
108+
109+
/// <inheritdoc />
110+
public void Dispose() { }
111+
}
112+
}
113+
114+
internal static class SinglyLinkedListKeyNodeExtensions
115+
{
116+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
117+
public static SinglyLinkedListKeyNode<TKey, TValue> Add<TKey, TValue>(this SinglyLinkedListKeyNode<TKey, TValue>? previous, in TKey key, in TValue value)
118+
{
119+
return new SinglyLinkedListKeyNode<TKey, TValue>(previous, in key, in value);
120+
}
121+
122+
public static TValue GetOrDefault<TKey, TValue>(this SinglyLinkedListKeyNode<TKey, TValue>? list, TKey key)
123+
{
124+
while (list != null)
125+
{
126+
if (ReferenceEquals(list.Key, key)) return list.Value;
127+
list = list.Next!;
128+
}
129+
130+
return default!;
131+
}
132+
}
133+
}

src/Singularity/Collections/ThreadSafeDictionary.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void Add(TKey key, TValue value)
2929
do
3030
{
3131
initialValue = _immutableHashTable;
32-
computedValue = _immutableHashTable.Add(key, value);
32+
computedValue = initialValue.Add(key, value);
3333
}
3434
while (initialValue != Interlocked.CompareExchange(ref _immutableHashTable, computedValue, initialValue));
3535
}

src/Singularity/Expressions/ExpressionGenerator.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,8 @@ private static object GetSingleton(Scoped containerScope, Expression expression)
108108

109109
public static Expression CreateScopedExpression<T>(Expression expression)
110110
{
111-
var factory = (Func<Scoped, T>)Expression.Lambda(expression, ScopeParameter).CompileFast();
112111
MethodInfo method = Scoped.GetOrAddScopedInstanceMethod.MakeGenericMethod(expression.Type);
113-
return Expression.Call(ScopeParameter, method, Expression.Constant(factory), Expression.Constant(expression.Type));
112+
return Expression.Call(ScopeParameter, method, Expression.Lambda(expression, ScopeParameter), Expression.Constant(expression.Type));
114113
}
115114
}
116115
}

src/Singularity/Lifetimes/ActionList.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public void Add(T obj)
3131
do
3232
{
3333
initialValue = _root;
34-
computedValue = _root.Add(obj);
34+
computedValue = initialValue.Add(obj);
3535
}
3636
while (initialValue != Interlocked.CompareExchange(ref _root, computedValue, initialValue));
3737
}

src/Singularity/Lifetimes/Scoped.cs

+43-35
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
using System.Linq;
44
using System.Reflection;
55
using System.Runtime.CompilerServices;
6+
using System.Threading;
67
using Singularity.Collections;
78

89
namespace Singularity
910
{
1011
/// <summary>
11-
/// Represents the scope in which instances will life.
12+
/// A light weight scope in which instances will life.
1213
/// </summary>
1314
public sealed class Scoped : IContainer
1415
{
1516
internal static readonly MethodInfo AddDisposableMethod = typeof(Scoped).GetRuntimeMethods().Single(x => x.Name == nameof(AddDisposable));
1617
internal static readonly MethodInfo AddFinalizerMethod = typeof(Scoped).GetRuntimeMethods().Single(x => x.Name == nameof(AddFinalizer));
1718
internal static readonly MethodInfo GetOrAddScopedInstanceMethod = typeof(Scoped).GetRuntimeMethods().Single(x => x.Name == nameof(GetOrAddScopedInstance));
18-
private static readonly Action<IDisposable> DisposeAction = x => x.Dispose();
1919

20-
private ThreadSafeDictionary<ServiceBinding, ActionList<object>> Finalizers { get; } = new ThreadSafeDictionary<ServiceBinding, ActionList<object>>();
21-
private ActionList<IDisposable> Disposables { get; } = new ActionList<IDisposable>(DisposeAction);
22-
private ThreadSafeDictionary<Type, object> ScopedInstances { get; } = new ThreadSafeDictionary<Type, object>();
20+
private SinglyLinkedListKeyNode<ServiceBinding, ActionList<object>> _finalizers;
21+
private SinglyLinkedListNode<IDisposable> _disposables;
22+
private SinglyLinkedListKeyNode<Type, object> _scopedInstances;
2323
private readonly Container _container;
2424

2525
internal Scoped(Container container)
@@ -72,47 +72,51 @@ public void LateInjectAll<T>(IEnumerable<T> instances)
7272
internal T GetOrAddScopedInstance<T>(Func<Scoped, T> factory, Type key)
7373
where T : class
7474
{
75-
var instance = (T)ScopedInstances.GetOrDefault(key);
76-
if (instance != null) return instance;
77-
78-
lock (ScopedInstances)
75+
SinglyLinkedListKeyNode<Type, object>? initialValue, computedValue;
76+
do
7977
{
80-
instance = (T)ScopedInstances.GetOrDefault(key);
78+
initialValue = _scopedInstances;
79+
var instance = (T)initialValue.GetOrDefault(key);
8180
if (instance != null) return instance;
82-
instance = factory(this);
83-
ScopedInstances.Add(key, instance);
84-
85-
return instance;
81+
T obj = factory(this); //There is a very slight chance that this instance is created more than once under heavy load.
82+
computedValue = initialValue.Add(key, obj);
8683
}
84+
while (initialValue != Interlocked.CompareExchange(ref _scopedInstances, computedValue, initialValue));
85+
86+
return (T)computedValue.Value;
8787
}
8888

8989
internal T AddDisposable<T>(T obj)
9090
where T : class, IDisposable
9191
{
92-
Disposables.Add(obj);
92+
SinglyLinkedListNode<IDisposable>? initialValue, computedValue;
93+
do
94+
{
95+
initialValue = _disposables;
96+
computedValue = initialValue.Add(obj);
97+
}
98+
while (initialValue != Interlocked.CompareExchange(ref _disposables, computedValue, initialValue));
9399
return obj;
94100
}
95101

96-
internal T AddFinalizer<T>(T obj, ServiceBinding serviceBinding)
102+
internal T AddFinalizer<T>(T obj, ServiceBinding key)
97103
where T : class
98104
{
99-
ActionList<object> list = Finalizers.GetOrDefault(serviceBinding);
100-
if (list != null)
105+
SinglyLinkedListKeyNode<ServiceBinding, ActionList<object>>? initialValue, computedValue;
106+
do
101107
{
102-
list.Add(obj);
103-
return obj;
104-
}
105-
106-
lock (Finalizers)
107-
{
108-
list = Finalizers.GetOrDefault(serviceBinding);
109-
if (list == null)
108+
initialValue = _finalizers;
109+
ActionList<object> list = initialValue.GetOrDefault(key);
110+
if (list != null)
110111
{
111-
list = new ActionList<object>(serviceBinding.Finalizer!);
112-
Finalizers.Add(serviceBinding, list);
112+
list.Add(obj);
113+
return obj;
113114
}
115+
116+
computedValue = initialValue.Add(key, new ActionList<object>(key.Finalizer!));
114117
}
115-
list.Add(obj!);
118+
while (initialValue != Interlocked.CompareExchange(ref _finalizers, computedValue, initialValue));
119+
computedValue.Value.Add(obj);
116120
return obj;
117121
}
118122

@@ -121,14 +125,18 @@ internal T AddFinalizer<T>(T obj, ServiceBinding serviceBinding)
121125
/// </summary>
122126
public void Dispose()
123127
{
124-
Disposables.Invoke();
128+
SinglyLinkedListNode<IDisposable>? disposables = _disposables;
129+
while (disposables != null)
130+
{
131+
disposables.Value.Dispose();
132+
disposables = disposables.Next!;
133+
}
125134

126-
if (Finalizers.Count > 0)
135+
SinglyLinkedListKeyNode<ServiceBinding, ActionList<object>>? finalizers = _finalizers;
136+
while (finalizers != null)
127137
{
128-
foreach (ActionList<object> objectActionList in Finalizers)
129-
{
130-
objectActionList.Invoke();
131-
}
138+
finalizers.Value.Invoke();
139+
finalizers = finalizers.Next;
132140
}
133141
}
134142
}

0 commit comments

Comments
 (0)