From 9ad7e68bee6db60b6ac4f8b931179d8695ba963b Mon Sep 17 00:00:00 2001 From: Eugene Sadovoi Date: Mon, 8 Apr 2019 00:10:18 -0400 Subject: [PATCH] Changing registrations engine --- src/Abstracts/Pipeline.cs | 7 + src/Registration/ContainerRegistration.cs | 28 ++- src/Registration/InternalRegistration.cs | 47 +++-- src/Storage/Metadata.cs | 104 ++++++++++ src/Storage/Registrations.cs | 17 +- src/Storage/Registry.cs | 67 +++++++ src/Strategies/LifetimeStrategy.cs | 33 +--- src/UnityContainer.IUnityContainer.cs | 6 +- src/UnityContainer.IUnityContainerAsync.cs | 177 ++++++++++++++++++ src/UnityContainer.Implementation.cs | 10 +- src/UnityContainer.Public.cs | 6 +- src/UnityContainer.Registration.cs | 118 +++++++++++- src/UnityContainer.Registry.cs | 83 ++++++++ src/UnityContainer.Resolution.cs | 42 ----- src/Utility/HashHelpers.cs | 21 +++ ...pecification.Tests.Diagnostic.Async.csproj | 22 +++ .../Unity.Specification.Async/Registration.cs | 32 ++++ .../Unity.Specification.Tests.Async.csproj | 22 +++ 18 files changed, 725 insertions(+), 117 deletions(-) create mode 100644 src/Abstracts/Pipeline.cs create mode 100644 src/Storage/Metadata.cs create mode 100644 src/Storage/Registry.cs create mode 100644 src/UnityContainer.IUnityContainerAsync.cs create mode 100644 src/UnityContainer.Registry.cs create mode 100644 tests/Unity.Diagnostic.Async/Unity.Specification.Tests.Diagnostic.Async.csproj create mode 100644 tests/Unity.Specification.Async/Registration.cs create mode 100644 tests/Unity.Specification.Async/Unity.Specification.Tests.Async.csproj diff --git a/src/Abstracts/Pipeline.cs b/src/Abstracts/Pipeline.cs new file mode 100644 index 00000000..36d8106f --- /dev/null +++ b/src/Abstracts/Pipeline.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; +using Unity.Resolution; + +namespace Unity.Abstracts +{ + public delegate Task PipelineDelegate(IUnityContainer container, params ResolverOverride[] overrides); +} diff --git a/src/Registration/ContainerRegistration.cs b/src/Registration/ContainerRegistration.cs index 07dcbe0e..cb9717ec 100644 --- a/src/Registration/ContainerRegistration.cs +++ b/src/Registration/ContainerRegistration.cs @@ -11,6 +11,27 @@ namespace Unity.Registration { #region Constructors + public ContainerRegistration(Type mappedTo, LifetimeManager lifetimeManager, InjectionMember[] injectionMembers = null) + { + Type = mappedTo; + Key = typeof(LifetimeManager); + Value = lifetimeManager; + LifetimeManager.InUse = true; + InjectionMembers = injectionMembers; + Next = null; + } + + + public ContainerRegistration(LinkedNode validators, LifetimeManager lifetimeManager, InjectionMember[] injectionMembers = null) + { + Type = null; + Key = typeof(LifetimeManager); + Value = lifetimeManager; + LifetimeManager.InUse = true; + InjectionMembers = injectionMembers; + Next = validators; + } + public ContainerRegistration(LinkedNode validators, Type mappedTo, LifetimeManager lifetimeManager, InjectionMember[] injectionMembers = null) { Type = mappedTo; @@ -32,13 +53,6 @@ namespace Unity.Registration /// public Type Type { get; } - /// - /// The lifetime manager for this registration. - /// - /// - /// This property will be null if this registration is for an open generic. - public LifetimeManager LifetimeManager => (LifetimeManager)Value; - #endregion } } diff --git a/src/Registration/InternalRegistration.cs b/src/Registration/InternalRegistration.cs index 4cc141da..1a62c0c1 100644 --- a/src/Registration/InternalRegistration.cs +++ b/src/Registration/InternalRegistration.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using System.Threading; using Unity.Injection; +using Unity.Lifetime; using Unity.Policy; using Unity.Storage; using Unity.Strategies; @@ -11,16 +13,28 @@ namespace Unity.Registration public class InternalRegistration : LinkedNode, IPolicySet { + #region Fields + + private int _refCount; + + #endregion + #region Constructors public InternalRegistration() { + Key = typeof(LifetimeManager); } public InternalRegistration(Type policyInterface, object policy) { - Key = policyInterface; - Value = policy; + Key = typeof(LifetimeManager); + Next = new LinkedNode + { + Key = policyInterface, + Value = policy, + Next = Next + }; } #endregion @@ -36,6 +50,17 @@ namespace Unity.Registration public Converter Map { get; set; } + // TODO: Streamline LifetimeManager usage + public LifetimeManager LifetimeManager + { + get => (LifetimeManager)Value; + set => Value = value; + } + + public virtual int AddRef() => Interlocked.Increment(ref _refCount); + + public virtual int Release() => Interlocked.Decrement(ref _refCount); + #endregion @@ -54,20 +79,12 @@ namespace Unity.Registration public virtual void Set(Type policyInterface, object policy) { - if (null == Value && null == Key) + Next = new LinkedNode { - Key = policyInterface; - Value = policy; - } - else - { - Next = new LinkedNode - { - Key = policyInterface, - Value = policy, - Next = Next - }; - } + Key = policyInterface, + Value = policy, + Next = Next + }; } public virtual void Clear(Type policyInterface) diff --git a/src/Storage/Metadata.cs b/src/Storage/Metadata.cs new file mode 100644 index 00000000..e11c95ba --- /dev/null +++ b/src/Storage/Metadata.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using Unity.Utility; + +namespace Unity.Storage +{ + [DebuggerDisplay("Metadata ({Count}) ")] + internal class Metadata + { + #region Fields + + private int[] Buckets; + private Entry[] Entries; + private int Count; + + #endregion + + + #region Constructors + + public Metadata() + { + Buckets = new int[37]; + Entries = new Entry[37]; + + HashHelpers.FillArray(Buckets, -1); + } + + #endregion + + + #region Public Members + + public void Add(Type key, int value) + { + // Check for existing + var hashCode = (key?.GetHashCode() ?? 0) & 0x7FFFFFFF; + var targetBucket = hashCode % Buckets.Length; + for (var i = Buckets[targetBucket]; i >= 0; i = Entries[i].Next) + { + ref var candidate = ref Entries[i]; + if (candidate.HashCode != hashCode || !Equals(candidate.Key, key)) continue; + + if (candidate.Data.Length == candidate.Count) + { + var source = candidate.Data; + candidate.Data = new int[2 * candidate.Data.Length]; + Array.Copy(source, 0, candidate.Data, 0, candidate.Count); + } + + candidate.Data[candidate.Count++] = value; + } + + // Grow if required + if ((Entries.Length - Count) < 100 && (float)Count / Entries.Length > 0.72f) + { + var entries = Entries; + var size = HashHelpers.GetPrime(2 * Buckets.Length); + + Buckets = new int[size]; + Entries = new Entry[size]; + + HashHelpers.FillArray(Buckets, -1); + + Array.Copy(entries, 0, Entries, 0, Count); + for (var i = 0; i < Count; i++) + { + var hash = Entries[i].HashCode; + if (hashCode < 0) continue; + + var bucket = hash % Buckets.Length; + Entries[i].Next = Buckets[bucket]; + Buckets[bucket] = i; + } + } + + // Add new entry + ref var entry = ref Entries[Count]; + entry.HashCode = hashCode; + entry.Next = Buckets[targetBucket]; + entry.Key = key; + entry.Count = 1; + entry.Data = new int[] { value, -1 }; + Buckets[targetBucket] = Count++; + } + + #endregion + + + #region Nested Types + + [DebuggerDisplay("Type='{Key}' Registrations='{Count}'")] + public struct Entry + { + public int HashCode; + public int Next; + public Type Key; + public int Count; + public int[] Data; + } + + #endregion + } +} diff --git a/src/Storage/Registrations.cs b/src/Storage/Registrations.cs index e929e3d1..fd1dfc91 100644 --- a/src/Storage/Registrations.cs +++ b/src/Storage/Registrations.cs @@ -7,7 +7,6 @@ using Unity.Utility; namespace Unity.Storage { - [SecuritySafeCritical] [DebuggerDisplay("Registrations ({Count}) ")] internal class Registrations : IRegistry> { @@ -32,23 +31,11 @@ namespace Unity.Storage public Registrations(int capacity) { var size = HashHelpers.GetPrime(capacity); + Buckets = new int[size]; Entries = new Entry[size]; -#if !NET40 - unsafe - { - fixed (int* bucketsPtr = Buckets) - { - int* ptr = bucketsPtr; - var end = bucketsPtr + Buckets.Length; - while (ptr < end) *ptr++ = -1; - } - } -#else - for(int i = 0; i < Buckets.Length; i++) - Buckets[i] = -1; -#endif + HashHelpers.FillArray(Buckets, -1); } public Registrations(int capacity, LinkedNode> head) diff --git a/src/Storage/Registry.cs b/src/Storage/Registry.cs new file mode 100644 index 00000000..4831e6d6 --- /dev/null +++ b/src/Storage/Registry.cs @@ -0,0 +1,67 @@ +using System; +using Unity.Policy; +using Unity.Resolution; +using Unity.Utility; + +namespace Unity.Storage +{ + public class Registry + { + #region Fields + + public readonly int[] Buckets; + public readonly Entry[] Entries; + public int Count; + + #endregion + + + #region Constructors + + public Registry(int capacity = 37) + { + var size = HashHelpers.GetPrime(capacity); + + Buckets = new int[size]; + Entries = new Entry[size]; + + HashHelpers.FillArray(Buckets, -1); + } + + public Registry(Registry registry) + : this(HashHelpers.GetPrime(registry.Entries.Length * 2)) + { + Array.Copy(registry.Entries, 0, Entries, 0, registry.Count); + for (var i = 0; i < registry.Count; i++) + { + var hashCode = Entries[i].HashCode; + if (hashCode < 0) continue; + + var bucket = hashCode % Buckets.Length; + Entries[i].Next = Buckets[bucket]; + Buckets[bucket] = i; + } + Count = registry.Count; + registry.Count = 0; + } + + #endregion + + + public bool RequireToGrow => (Entries.Length - Count) < 100 && + (float)Count / Entries.Length > 0.72f; + + + #region Nested Types + + public struct Entry + { + public int HashCode; + public NamedType Key; + public int Next; + public IPolicySet Reference; + } + + #endregion + } +} diff --git a/src/Strategies/LifetimeStrategy.cs b/src/Strategies/LifetimeStrategy.cs index 788c2bc1..a711f483 100644 --- a/src/Strategies/LifetimeStrategy.cs +++ b/src/Strategies/LifetimeStrategy.cs @@ -28,38 +28,13 @@ namespace Unity.Strategies { LifetimeManager policy = null; - if (context.Registration is ContainerRegistration registration) + if (context.Registration is InternalRegistration registration) policy = registration.LifetimeManager; if (null == policy || policy is PerResolveLifetimeManager) policy = (LifetimeManager)context.Get(typeof(LifetimeManager)); - if (null == policy) - { -#if NETSTANDARD1_0 || NETCOREAPP1_0 - if (!context.RegistrationType.GetTypeInfo().IsGenericType) return; -#else - if (!context.RegistrationType.IsGenericType) return; -#endif - var manager = (LifetimeManager)context.Get(context.Type.GetGenericTypeDefinition(), - context.Name, typeof(LifetimeManager)); - if (null == manager) return; - lock (_genericLifetimeManagerLock) - { - // check whether the policy for closed-generic has been added since first checked - policy = (LifetimeManager)context.Registration.Get(typeof(LifetimeManager)); - if (null == policy) - { - policy = manager.CreateLifetimePolicy(); - context.Registration.Set(typeof(LifetimeManager), policy); - - if (policy is IDisposable) - { - context.Lifetime.Add(policy); - } - } - } - } + if (null == policy) return; if (policy is SynchronizedLifetimeManager recoveryPolicy) context.RequiresRecovery = recoveryPolicy; @@ -76,7 +51,7 @@ namespace Unity.Strategies { LifetimeManager policy = null; - if (context.Registration is ContainerRegistration registration) + if (context.Registration is InternalRegistration registration) policy = registration.LifetimeManager; if (null == policy || policy is PerResolveLifetimeManager) @@ -92,7 +67,7 @@ namespace Unity.Strategies public override bool RequiredToBuildType(IUnityContainer container, Type type, InternalRegistration registration, params InjectionMember[] injectionMembers) { - var policy = registration.Get(typeof(LifetimeManager)); + var policy = registration.LifetimeManager; if (null != policy) { return policy is TransientLifetimeManager ? false : true; diff --git a/src/UnityContainer.IUnityContainer.cs b/src/UnityContainer.IUnityContainer.cs index 2b0f73e8..317c78f1 100644 --- a/src/UnityContainer.IUnityContainer.cs +++ b/src/UnityContainer.IUnityContainer.cs @@ -45,7 +45,7 @@ namespace Unity var registration = new ContainerRegistration(_validators, typeTo, (LifetimeManager)lifetimeManager, injectionMembers); // Add or replace existing - var previous = container.Register(registeredType, name, registration); + var previous = container.RegisterLegacy(registeredType, name, registration); if (previous is ContainerRegistration old && old.LifetimeManager is IDisposable disposable) { @@ -125,7 +125,7 @@ namespace Unity var registration = new ContainerRegistration(null, mappedToType, ((LifetimeManager)lifetimeManager)); // Add or replace existing - var previous = container.Register(typeFrom, name, registration); + var previous = container.RegisterLegacy(typeFrom, name, registration); if (previous is ContainerRegistration old && old.LifetimeManager is IDisposable disposable) { @@ -183,7 +183,7 @@ namespace Unity var registration = new ContainerRegistration(_validators, type, ((LifetimeManager)lifetimeManager), injectionMembers); // Add or replace existing - var previous = container.Register(type, name, registration); + var previous = container.RegisterLegacy(type, name, registration); if (previous is ContainerRegistration old && old.LifetimeManager is IDisposable disposable) { diff --git a/src/UnityContainer.IUnityContainerAsync.cs b/src/UnityContainer.IUnityContainerAsync.cs new file mode 100644 index 00000000..d08fa365 --- /dev/null +++ b/src/UnityContainer.IUnityContainerAsync.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Unity.Injection; +using Unity.Lifetime; +using Unity.Registration; +using Unity.Resolution; + +namespace Unity +{ + public partial class UnityContainer : IUnityContainerAsync + { + #region Registration + + #region Type + + /// + IUnityContainer IUnityContainerAsync.RegisterType(IEnumerable interfaces, Type type, string name, ITypeLifetimeManager lifetimeManager, params InjectionMember[] injectionMembers) + { + throw new NotImplementedException(); + } + + #endregion + + + #region Factory + + /// + IUnityContainer IUnityContainerAsync.RegisterFactory(IEnumerable interfaces, string name, Func factory, IFactoryLifetimeManager lifetimeManager) + { + // Validate input + // TODO: Move to diagnostic + + if (null == interfaces) throw new ArgumentNullException(nameof(interfaces)); + if (null == factory) throw new ArgumentNullException(nameof(factory)); + if (null == lifetimeManager) lifetimeManager = TransientLifetimeManager.Instance; + if (((LifetimeManager)lifetimeManager).InUse) throw new InvalidOperationException(LifetimeManagerInUse); + + // Create registration and add to appropriate storage + var container = lifetimeManager is SingletonLifetimeManager ? _root : this; + + // TODO: InjectionFactory + #pragma warning disable CS0618 + var injectionFactory = new InjectionFactory(factory); + #pragma warning restore CS0618 + + var injectionMembers = new InjectionMember[] { injectionFactory }; + var registration = new ContainerRegistration(_validators, (LifetimeManager)lifetimeManager, injectionMembers); + + // Add Injection Members + //injectionFactory.AddPolicies( + // type, type, name, ref registration); + + // Register interfaces + var replaced = container.AddOrReplaceRegistrations(interfaces, name, registration) + .ToArray(); + + // Release replaced registrations + if (0 != replaced.Length) + { + Task.Factory.StartNew(() => + { + foreach (InternalRegistration previous in replaced) + { + if (0 == previous.Release() && previous.LifetimeManager is IDisposable disposable) + { + // Dispose replaced lifetime manager + container.LifetimeContainer.Remove(disposable); + disposable.Dispose(); + } + } + }); + } + + return this; + + } + + #endregion + + + #region Instance + + /// + IUnityContainer IUnityContainerAsync.RegisterInstance(IEnumerable interfaces, string name, object instance, IInstanceLifetimeManager lifetimeManager) + { + // Validate input + // TODO: Move to diagnostic + + if (null == interfaces && null == instance) throw new ArgumentNullException(nameof(interfaces)); + + // Validate lifetime manager + if (null == lifetimeManager) lifetimeManager = new ContainerControlledLifetimeManager(); + if (((LifetimeManager)lifetimeManager).InUse) throw new InvalidOperationException(LifetimeManagerInUse); + ((LifetimeManager)lifetimeManager).SetValue(instance, LifetimeContainer); + + // Create registration and add to appropriate storage + var mappedToType = instance?.GetType(); + var container = lifetimeManager is SingletonLifetimeManager ? _root : this; + var registration = new ContainerRegistration(mappedToType, (LifetimeManager)lifetimeManager); + + // Register interfaces + var replaced = container.AddOrReplaceRegistrations(interfaces, name, registration) + .ToArray(); + + // Release replaced registrations + if (0 != replaced.Length) + { + Task.Factory.StartNew(() => + { + foreach (InternalRegistration previous in replaced) + { + if (0 == previous.Release() && previous.LifetimeManager is IDisposable disposable) + { + // Dispose replaced lifetime manager + container.LifetimeContainer.Remove(disposable); + disposable.Dispose(); + } + } + }); + } + + return this; + } + + #endregion + + + #endregion + + + #region Registrations + + /// + IEnumerable IUnityContainerAsync.Registrations + { + get + { + throw new NotImplementedException(); + } + } + + /// + bool IUnityContainerAsync.IsRegistered(Type type, string name) + { + throw new NotImplementedException(); + } + + #endregion + + + #region Hierarchy + + /// + IUnityContainer IUnityContainerAsync.Parent => throw new NotImplementedException(); + + /// + IUnityContainer IUnityContainerAsync.CreateChildContainer() + { + throw new NotImplementedException(); + } + + #endregion + + + #region Resolution + + /// + Task IUnityContainerAsync.Resolve(Type type, string name, params ResolverOverride[] overrides) + { + return null;// _getPipeline(type, name).Invoke(this, overrides); + } + + #endregion + } +} diff --git a/src/UnityContainer.Implementation.cs b/src/UnityContainer.Implementation.cs index 74cf258b..42fc48ce 100644 --- a/src/UnityContainer.Implementation.cs +++ b/src/UnityContainer.Implementation.cs @@ -58,7 +58,7 @@ namespace Unity internal Func GetRegistration; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - internal Func Register; + internal Func RegisterLegacy; [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal GetPolicyDelegate GetPolicy; @@ -98,6 +98,8 @@ namespace Unity _root = _parent._root; SetDefaultPolicies = parent.SetDefaultPolicies; + // Registry + // Methods _get = _parent._get; _getGenericRegistration = _parent._getGenericRegistration; @@ -105,7 +107,7 @@ namespace Unity IsTypeExplicitlyRegistered = _parent.IsTypeExplicitlyRegistered; GetRegistration = _parent.GetRegistration; - Register = CreateAndSetOrUpdate; + RegisterLegacy = CreateAndSetOrUpdate; GetPolicy = parent.GetPolicy; SetPolicy = CreateAndSetPolicy; ClearPolicy = delegate { }; @@ -242,7 +244,7 @@ namespace Unity SetupChildContainerBehaviors(); } - return AddOrUpdate(type, name, registration); + return AddOrUpdateLegacy(type, name, registration); } private void SetupChildContainerBehaviors() @@ -254,7 +256,7 @@ namespace Unity _registrations = new Registrations(ContainerInitialCapacity); Set(null, null, Defaults); - Register = AddOrUpdate; + RegisterLegacy = AddOrUpdateLegacy; GetPolicy = Get; SetPolicy = Set; ClearPolicy = Clear; diff --git a/src/UnityContainer.Public.cs b/src/UnityContainer.Public.cs index f9b7ec21..59c9f161 100644 --- a/src/UnityContainer.Public.cs +++ b/src/UnityContainer.Public.cs @@ -32,6 +32,9 @@ namespace Unity // Context _context = new ContainerContext(this); + // Registry + Register = InitAndAdd; + // Methods _get = Get; _getGenericRegistration = GetOrAddGeneric; @@ -39,7 +42,7 @@ namespace Unity IsTypeExplicitlyRegistered = IsTypeTypeExplicitlyRegisteredLocally; GetRegistration = GetOrAdd; - Register = AddOrUpdate; + RegisterLegacy = AddOrUpdateLegacy; GetPolicy = Get; SetPolicy = Set; ClearPolicy = Clear; @@ -78,6 +81,7 @@ namespace Unity // Register this instance ((IUnityContainer)this).RegisterInstance(typeof(IUnityContainer), null, this, new ContainerLifetimeManager()); + ((IUnityContainerAsync)this).RegisterInstance(new[] { typeof(IUnityContainer) }, null, this, new ContainerLifetimeManager()); } #endregion diff --git a/src/UnityContainer.Registration.cs b/src/UnityContainer.Registration.cs index 575ac9cb..0c134681 100644 --- a/src/UnityContainer.Registration.cs +++ b/src/UnityContainer.Registration.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using Unity.Policy; using Unity.Registration; +using Unity.Resolution; using Unity.Storage; namespace Unity @@ -19,8 +21,19 @@ namespace Unity #endregion + #region Delegates + + private delegate IPolicySet RegisterDelegate(ref NamedType key, InternalRegistration registration); + + #endregion + + #region Registration Fields + // Register single type + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private RegisterDelegate Register; + internal IPolicySet Defaults; private readonly object _syncRoot = new object(); private LinkedNode _validators; @@ -339,9 +352,112 @@ namespace Unity #endregion + #region Dynamic Registrations + + private IPolicySet GetDynamicRegistration(Type type, string name) + { + var registration = _get(type, name); + if (null != registration) return registration; + + var info = type.GetTypeInfo(); + return !info.IsGenericType + ? _root.GetOrAdd(type, name) + : GetOrAddGeneric(type, name, info.GetGenericTypeDefinition()); + } + + private IPolicySet CreateRegistration(Type type, string name) + { + var registration = new InternalRegistration(type, name); + + if (type.GetTypeInfo().IsGenericType) + { + var factory = (InternalRegistration)_get(type.GetGenericTypeDefinition(), name); + if (null != factory) + { + registration.InjectionMembers = factory.InjectionMembers; + registration.Map = factory.Map; + var manager = factory.LifetimeManager; + if (null != manager) + { + var policy = manager.CreateLifetimePolicy(); + registration.LifetimeManager = policy; + if (policy is IDisposable) LifetimeContainer.Add(policy); + } + } + } + + registration.BuildChain = GetBuilders(type, registration); + return registration; + } + + private IPolicySet CreateRegistration(Type type, Type policyInterface, object policy) + { + var registration = new InternalRegistration(policyInterface, policy); + registration.BuildChain = GetBuilders(type, registration); + return registration; + } + + #endregion + + #region Registration manipulation - private IPolicySet AddOrUpdate(Type type, string name, InternalRegistration registration) + private IEnumerable AddOrReplaceRegistrations(IEnumerable interfaces, string name, ContainerRegistration registration) + { + NamedType key; + int count = 0; + + key.Name = name; + + if (null != interfaces) + { + foreach (var type in interfaces) + { + // Type to register + key.Type = type; + + // Add or replace existing + var previous = Register(ref key, registration); + + // Add reference count + registration.AddRef(); + + // Allow reference adjustment and disposal + if (previous is ContainerRegistration old && + old.LifetimeManager is IDisposable disposable) + { + yield return previous; + } + + count++; + } + } + + if (0 == count) + { + // TODO: Move to diagnostic + if (null == registration.Type) throw new ArgumentNullException(nameof(interfaces)); + + // Type to register + key.Type = registration.Type; + + // Add or replace existing + var previous = Register(ref key, registration); + + // Add reference count + registration.AddRef(); + + // Allow reference adjustment and disposal + if (previous is ContainerRegistration old && + old.LifetimeManager is IDisposable disposable) + { + yield return previous; + } + } + } + + + private IPolicySet AddOrUpdateLegacy(Type type, string name, InternalRegistration registration) { var collisions = 0; var hashCode = (type?.GetHashCode() ?? 0) & 0x7FFFFFFF; diff --git a/src/UnityContainer.Registry.cs b/src/UnityContainer.Registry.cs new file mode 100644 index 00000000..d8c89622 --- /dev/null +++ b/src/UnityContainer.Registry.cs @@ -0,0 +1,83 @@ +using Unity.Policy; +using Unity.Registration; +using Unity.Resolution; +using Unity.Storage; + +namespace Unity +{ + public partial class UnityContainer + { + #region Fields + + private Registry _registry; + private Metadata _metadata; + + #endregion + + + #region + + private IPolicySet InitAndAdd(ref NamedType key, InternalRegistration registration) + { + lock (_syncRoot) + { + if (Register == InitAndAdd) + { + _registry = new Registry(); + _metadata = new Metadata(); + + Register = AddOrReplace; + } + } + + return Register(ref key, registration); + } + + private IPolicySet AddOrReplace(ref NamedType key, InternalRegistration registration) + { + var hashCode = key.GetHashCode(); + var targetBucket = hashCode % _registry.Buckets.Length; + var collisions = 0; + + lock (_syncRoot) + { + for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next) + { + ref var candidate = ref _registry.Entries[i]; + if (candidate.HashCode != hashCode || + candidate.Key.Type != key.Type) + { + collisions++; + continue; + } + + var existing = candidate.Reference; + + candidate.Reference = registration; + + return existing; + } + + if (_registry.RequireToGrow || ListToHashCutPoint < collisions) + { + _registry = new Registry(_registry); + targetBucket = hashCode % _registry.Buckets.Length; + } + + ref var entry = ref _registry.Entries[_registry.Count]; + entry.HashCode = hashCode; + entry.Next = _registry.Buckets[targetBucket]; + entry.Key = key; + entry.Reference = registration; + var position = _registry.Count++; + _registry.Buckets[targetBucket] = position; + _metadata.Add(key.Type, position); + + return null; + } + } + + + #endregion + } +} diff --git a/src/UnityContainer.Resolution.cs b/src/UnityContainer.Resolution.cs index 8e59a643..11a014f4 100644 --- a/src/UnityContainer.Resolution.cs +++ b/src/UnityContainer.Resolution.cs @@ -26,48 +26,6 @@ namespace Unity [SecuritySafeCritical] public partial class UnityContainer { - #region Dynamic Registrations - - private IPolicySet GetDynamicRegistration(Type type, string name) - { - var registration = _get(type, name); - if (null != registration) return registration; - - var info = type.GetTypeInfo(); - return !info.IsGenericType - ? _root.GetOrAdd(type, name) - : GetOrAddGeneric(type, name, info.GetGenericTypeDefinition()); - } - - private IPolicySet CreateRegistration(Type type, string name) - { - - var registration = new InternalRegistration(type, name); - - if (type.GetTypeInfo().IsGenericType) - { - var factory = (InternalRegistration)_get(type.GetGenericTypeDefinition(), name); - if (null != factory) - { - registration.InjectionMembers = factory.InjectionMembers; - registration.Map = factory.Map; - } - } - - registration.BuildChain = GetBuilders(type, registration); - return registration; - } - - private IPolicySet CreateRegistration(Type type, Type policyInterface, object policy) - { - var registration = new InternalRegistration(policyInterface, policy); - registration.BuildChain = GetBuilders(type, registration); - return registration; - } - - #endregion - - #region Resolving Collections internal static object ResolveEnumerable(ref BuilderContext context) diff --git a/src/Utility/HashHelpers.cs b/src/Utility/HashHelpers.cs index 940516d9..51f36409 100644 --- a/src/Utility/HashHelpers.cs +++ b/src/Utility/HashHelpers.cs @@ -1,7 +1,9 @@ using System; +using System.Security; namespace Unity.Utility { + [SecuritySafeCritical] internal static class HashHelpers { // Table of prime numbers to use as hash table sizes. @@ -74,5 +76,24 @@ namespace Unity.Utility // This is the maximum prime smaller than Array.MaxArrayLength public const int MaxPrimeArrayLength = 0x7FEFFFFD; + + public static void FillArray(int[] array, int value) + { +#if !NET40 + unsafe + { + fixed (int* bucketsPtr = array) + { + int* ptr = bucketsPtr; + var end = bucketsPtr + array.Length; + while (ptr < end) *ptr++ = value; + } + } +#else + for(int i = 0; i < array.Length; i++) + array[i] = value; +#endif + } + } } diff --git a/tests/Unity.Diagnostic.Async/Unity.Specification.Tests.Diagnostic.Async.csproj b/tests/Unity.Diagnostic.Async/Unity.Specification.Tests.Diagnostic.Async.csproj new file mode 100644 index 00000000..59ebac67 --- /dev/null +++ b/tests/Unity.Diagnostic.Async/Unity.Specification.Tests.Diagnostic.Async.csproj @@ -0,0 +1,22 @@ + + + + net461 + false + true + ..\..\src\package.snk + false + + + + + + + + + + + + + + diff --git a/tests/Unity.Specification.Async/Registration.cs b/tests/Unity.Specification.Async/Registration.cs new file mode 100644 index 00000000..888f023c --- /dev/null +++ b/tests/Unity.Specification.Async/Registration.cs @@ -0,0 +1,32 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Unity; + +namespace Registration +{ + [TestClass] + public class Type : Unity.Specification.Async.Registration.Types.SpecificationTests + { + public override IUnityContainerAsync GetContainer() + { + return new UnityContainer(); + } + } + + [TestClass] + public class Factory : Unity.Specification.Async.Registration.Factory.SpecificationTests + { + public override IUnityContainerAsync GetContainer() + { + return new UnityContainer(); + } + } + + [TestClass] + public class Instance : Unity.Specification.Async.Registration.Instance.SpecificationTests + { + public override IUnityContainerAsync GetContainer() + { + return new UnityContainer(); + } + } +} diff --git a/tests/Unity.Specification.Async/Unity.Specification.Tests.Async.csproj b/tests/Unity.Specification.Async/Unity.Specification.Tests.Async.csproj new file mode 100644 index 00000000..08f36c20 --- /dev/null +++ b/tests/Unity.Specification.Async/Unity.Specification.Tests.Async.csproj @@ -0,0 +1,22 @@ + + + + net461 + false + true + ..\..\src\package.snk + false + + + + + + + + + + + + + +