From e0ea6e2cd55a72595c275c2954e8ecf615d694bd Mon Sep 17 00:00:00 2001 From: Eugene Sadovoi Date: Mon, 8 Apr 2019 23:17:56 -0400 Subject: [PATCH] new registry --- src/Builder/Context/BuilderContext.cs | 4 +- src/Storage/Metadata.cs | 24 ++- src/Storage/Registry.cs | 4 +- src/UnityContainer.IUnityContainer.cs | 63 +++++--- src/UnityContainer.IUnityContainerAsync.cs | 3 + src/UnityContainer.Implementation.cs | 9 -- src/UnityContainer.Public.cs | 3 - src/UnityContainer.Registration.cs | 178 ++++++++------------- src/UnityContainer.Registry.cs | 90 +++++++++-- 9 files changed, 219 insertions(+), 159 deletions(-) diff --git a/src/Builder/Context/BuilderContext.cs b/src/Builder/Context/BuilderContext.cs index fdc4eef3..dca57f60 100644 --- a/src/Builder/Context/BuilderContext.cs +++ b/src/Builder/Context/BuilderContext.cs @@ -64,7 +64,9 @@ namespace Unity.Builder } } - return Resolve(type, name, (InternalRegistration)((UnityContainer)Container).GetRegistration(type, name)); + NamedType key = new NamedType { Name = name, Type = type }; + + return Resolve(type, name, (InternalRegistration)((UnityContainer)Container).GetRegistration(ref key)); } #endregion diff --git a/src/Storage/Metadata.cs b/src/Storage/Metadata.cs index e11c95ba..a863b32f 100644 --- a/src/Storage/Metadata.cs +++ b/src/Storage/Metadata.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Unity.Utility; namespace Unity.Storage @@ -12,6 +14,7 @@ namespace Unity.Storage private int[] Buckets; private Entry[] Entries; private int Count; + public static readonly int[] Empty = new int[0]; #endregion @@ -76,14 +79,33 @@ namespace Unity.Storage // 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 }; + entry.HashCode = hashCode; Buckets[targetBucket] = Count++; } + // TODO: Performance + public IEnumerable Get(Type key) + { + 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; + + var count = candidate.Count; + var data = candidate.Data; + + return data.Take(count); + } + + return Empty; + } + #endregion diff --git a/src/Storage/Registry.cs b/src/Storage/Registry.cs index 4831e6d6..8eb25d0b 100644 --- a/src/Storage/Registry.cs +++ b/src/Storage/Registry.cs @@ -1,5 +1,5 @@ using System; -using Unity.Policy; +using Unity.Registration; using Unity.Resolution; using Unity.Utility; @@ -59,7 +59,7 @@ namespace Unity.Storage public int HashCode; public NamedType Key; public int Next; - public IPolicySet Reference; + public InternalRegistration Registration; } #endregion diff --git a/src/UnityContainer.IUnityContainer.cs b/src/UnityContainer.IUnityContainer.cs index 317c78f1..6ea3a43a 100644 --- a/src/UnityContainer.IUnityContainer.cs +++ b/src/UnityContainer.IUnityContainer.cs @@ -44,20 +44,23 @@ namespace Unity var container = lifetimeManager is SingletonLifetimeManager ? _root : this; var registration = new ContainerRegistration(_validators, typeTo, (LifetimeManager)lifetimeManager, injectionMembers); + // If Disposable add to container's lifetime + if (lifetimeManager is IDisposable disposableManager) + container.LifetimeContainer.Add(disposableManager); + // Add or replace existing - var previous = container.RegisterLegacy(registeredType, name, registration); - if (previous is ContainerRegistration old && - old.LifetimeManager is IDisposable disposable) + NamedType key = new NamedType { Name = name, Type = registeredType }; + var previous = container.Register(ref key, registration); + + // Allow reference adjustment and disposal + if (null != previous && 0 == previous.Release() + && previous.LifetimeManager is IDisposable disposable) { // Dispose replaced lifetime manager container.LifetimeContainer.Remove(disposable); disposable.Dispose(); } - // If Disposable add to container's lifetime - if (lifetimeManager is IDisposable disposableManager) - container.LifetimeContainer.Add(disposableManager); - // Add Injection Members if (null != injectionMembers && injectionMembers.Length > 0) { @@ -109,12 +112,12 @@ namespace Unity IUnityContainer IUnityContainer.RegisterInstance(Type type, string name, object instance, IInstanceLifetimeManager lifetimeManager) { var mappedToType = instance?.GetType(); - var typeFrom = type ?? mappedToType; + var registeredType = type ?? mappedToType; try { // Validate input - if (null == typeFrom) throw new InvalidOperationException($"At least one of Type arguments '{nameof(type)}' or '{nameof(instance)}' must be not 'null'"); + if (null == registeredType) throw new InvalidOperationException($"At least one of Type arguments '{nameof(type)}' or '{nameof(instance)}' must be not 'null'"); if (null == lifetimeManager) lifetimeManager = new ContainerControlledLifetimeManager(); if (((LifetimeManager)lifetimeManager).InUse) throw new InvalidOperationException(LifetimeManagerInUse); @@ -124,26 +127,29 @@ namespace Unity var container = lifetimeManager is SingletonLifetimeManager ? _root : this; var registration = new ContainerRegistration(null, mappedToType, ((LifetimeManager)lifetimeManager)); - // Add or replace existing - var previous = container.RegisterLegacy(typeFrom, name, registration); - if (previous is ContainerRegistration old && - old.LifetimeManager is IDisposable disposable) + // If Disposable add to container's lifetime + if (lifetimeManager is IDisposable manager) + container.LifetimeContainer.Add(manager); + + // Register type + NamedType key = new NamedType { Name = name, Type = registeredType }; + var previous = container.Register(ref key, registration); + + // Allow reference adjustment and disposal + if (null != previous && 0 == previous.Release() + && previous.LifetimeManager is IDisposable disposable) { // Dispose replaced lifetime manager container.LifetimeContainer.Remove(disposable); disposable.Dispose(); } - // If Disposable add to container's lifetime - if (lifetimeManager is IDisposable manager) - container.LifetimeContainer.Add(manager); - // Check what strategies to run registration.BuildChain = _strategiesChain.ToArray() - .Where(strategy => strategy.RequiredToResolveInstance(this, registration)) - .ToArray(); + .Where(strategy => strategy.RequiredToResolveInstance(this, registration)) + .ToArray(); // Raise event - container.RegisteringInstance?.Invoke(this, new RegisterInstanceEventArgs(typeFrom, instance, + container.RegisteringInstance?.Invoke(this, new RegisterInstanceEventArgs(registeredType, instance, name, ((LifetimeManager)lifetimeManager))); } catch (Exception ex) @@ -153,7 +159,7 @@ namespace Unity if (null != name) parts.Add($" '{name}'"); if (null != lifetimeManager && !(lifetimeManager is TransientLifetimeManager)) parts.Add(lifetimeManager.ToString()); - var message = $"Error in RegisterInstance<{typeFrom?.Name}>({string.Join(", ", parts)})"; + var message = $"Error in RegisterInstance<{registeredType?.Name}>({string.Join(", ", parts)})"; throw new InvalidOperationException(message, ex); } @@ -183,9 +189,12 @@ namespace Unity var registration = new ContainerRegistration(_validators, type, ((LifetimeManager)lifetimeManager), injectionMembers); // Add or replace existing - var previous = container.RegisterLegacy(type, name, registration); - if (previous is ContainerRegistration old && - old.LifetimeManager is IDisposable disposable) + NamedType key = new NamedType { Name = name, Type = type }; + var previous = container.Register(ref key, registration); + + // Allow reference adjustment and disposal + if (null != previous && 0 == previous.Release() + && previous.LifetimeManager is IDisposable disposable) { // Dispose replaced lifetime manager container.LifetimeContainer.Remove(disposable); @@ -234,7 +243,8 @@ namespace Unity if (null == type) throw new ArgumentNullException(nameof(type)); name = string.IsNullOrEmpty(name) ? null : name; - var registration = (InternalRegistration)GetRegistration(type, name); + NamedType key = new NamedType { Name = name, Type = type }; + var registration = (InternalRegistration)GetRegistration(ref key); var context = new BuilderContext { List = new PolicyList(), @@ -266,7 +276,8 @@ namespace Unity // Validate if they are assignable if (null != existing && null != TypeValidator) TypeValidator(type, existing.GetType()); - var registration = (InternalRegistration)GetRegistration(type, name); + NamedType key = new NamedType { Name = name, Type = type }; + var registration = (InternalRegistration)GetRegistration(ref key); var context = new BuilderContext { List = new PolicyList(), diff --git a/src/UnityContainer.IUnityContainerAsync.cs b/src/UnityContainer.IUnityContainerAsync.cs index d08fa365..5a8aaa77 100644 --- a/src/UnityContainer.IUnityContainerAsync.cs +++ b/src/UnityContainer.IUnityContainerAsync.cs @@ -100,6 +100,9 @@ namespace Unity var container = lifetimeManager is SingletonLifetimeManager ? _root : this; var registration = new ContainerRegistration(mappedToType, (LifetimeManager)lifetimeManager); + // If Disposable add to container's lifetime + if (lifetimeManager is IDisposable manager) container.LifetimeContainer.Add(manager); + // Register interfaces var replaced = container.AddOrReplaceRegistrations(interfaces, name, registration) .ToArray(); diff --git a/src/UnityContainer.Implementation.cs b/src/UnityContainer.Implementation.cs index 42fc48ce..419be5ab 100644 --- a/src/UnityContainer.Implementation.cs +++ b/src/UnityContainer.Implementation.cs @@ -54,11 +54,6 @@ namespace Unity private event EventHandler ChildContainerCreated; // Methods - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - internal Func GetRegistration; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - internal Func RegisterLegacy; [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal GetPolicyDelegate GetPolicy; @@ -107,7 +102,6 @@ namespace Unity IsTypeExplicitlyRegistered = _parent.IsTypeExplicitlyRegistered; GetRegistration = _parent.GetRegistration; - RegisterLegacy = CreateAndSetOrUpdate; GetPolicy = parent.GetPolicy; SetPolicy = CreateAndSetPolicy; ClearPolicy = delegate { }; @@ -256,13 +250,10 @@ namespace Unity _registrations = new Registrations(ContainerInitialCapacity); Set(null, null, Defaults); - RegisterLegacy = AddOrUpdateLegacy; GetPolicy = Get; SetPolicy = Set; ClearPolicy = Clear; - GetRegistration = GetDynamicRegistration; - _get = (type, name) => Get(type, name) ?? _parent._get(type, name); _getGenericRegistration = GetOrAddGeneric; IsTypeExplicitlyRegistered = IsTypeTypeExplicitlyRegisteredLocally; diff --git a/src/UnityContainer.Public.cs b/src/UnityContainer.Public.cs index 59c9f161..c8476096 100644 --- a/src/UnityContainer.Public.cs +++ b/src/UnityContainer.Public.cs @@ -41,8 +41,6 @@ namespace Unity _isExplicitlyRegistered = IsExplicitlyRegisteredLocally; IsTypeExplicitlyRegistered = IsTypeTypeExplicitlyRegisteredLocally; - GetRegistration = GetOrAdd; - RegisterLegacy = AddOrUpdateLegacy; GetPolicy = Get; SetPolicy = Set; ClearPolicy = Clear; @@ -81,7 +79,6 @@ 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 0c134681..b1a3b4ac 100644 --- a/src/UnityContainer.Registration.cs +++ b/src/UnityContainer.Registration.cs @@ -23,7 +23,19 @@ namespace Unity #region Delegates - private delegate IPolicySet RegisterDelegate(ref NamedType key, InternalRegistration registration); + private delegate InternalRegistration RegisterDelegate(ref NamedType key, InternalRegistration registration); + internal delegate InternalRegistration GetRegistrationDelegate(ref NamedType key); + + #endregion + + + #region Registration Methods + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private RegisterDelegate Register; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + internal GetRegistrationDelegate GetRegistration; #endregion @@ -31,8 +43,6 @@ namespace Unity #region Registration Fields // Register single type - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private RegisterDelegate Register; internal IPolicySet Defaults; private readonly object _syncRoot = new object(); @@ -191,40 +201,15 @@ namespace Unity var seed = null != container._parent ? GetRegistrations(container._parent) : new RegistrationSet(); - if (null == container._registrations) return seed; + if (null == container._registry) return seed; - var length = container._registrations.Count; - var entries = container._registrations.Entries; + var registry = container._registry; - for (var i = null == container._parent ? GetStartIndex() : 0; i < length; i++) + for (var i = null == container._parent ? GetStartIndex() : 0; i < registry.Count; i++) { - ref var entry = ref entries[i]; - var registry = entry.Value; - - switch (registry) - { - case LinkedRegistry linkedRegistry: - for (var node = (LinkedNode)linkedRegistry; null != node; node = node.Next) - { - if (node.Value is ContainerRegistration containerRegistration) - seed.Add(entry.Key, node.Key, containerRegistration); - } - break; - - case HashRegistry hashRegistry: - var count = hashRegistry.Count; - var nodes = hashRegistry.Entries; - for (var j = 0; j < count; j++) - { - ref var refNode = ref nodes[j]; - if (refNode.Value is ContainerRegistration containerRegistration) - seed.Add(entry.Key, refNode.Key, containerRegistration); - } - break; - - default: - throw new InvalidOperationException("Unknown type of registry"); - } + ref var entry = ref registry.Entries[i]; + if (entry.Registration is ContainerRegistration containerRegistration) + seed.Add(entry.Key.Type, entry.Key.Name, containerRegistration); } return seed; @@ -232,9 +217,9 @@ namespace Unity int GetStartIndex() { int start = -1; - while (++start < length) + while (++start < registry.Count) { - if (typeof(IUnityContainer) != container._registrations.Entries[start].Key) + if (typeof(IUnityContainer) != container._registry.Entries[start].Key.Type) continue; return start; } @@ -248,36 +233,17 @@ namespace Unity var seed = null != container._parent ? GetRegistrations(container._parent, types) : new RegistrationSet(); - if (null == container._registrations) return seed; + if (null == container._registry) return seed; + + var registry = container._registry; foreach (var type in types) { - var registry = container.Get(type); - if (null == registry?.Values) continue; - - switch (registry) + foreach (var i in container._metadata.Get(type)) { - case LinkedRegistry linkedRegistry: - for (var node = (LinkedNode)linkedRegistry; null != node; node = node.Next) - { - if (node.Value is ContainerRegistration containerRegistration) - seed.Add(type, node.Key, containerRegistration); - } - break; - - case HashRegistry hashRegistry: - var count = hashRegistry.Count; - var nodes = hashRegistry.Entries; - for (var j = 0; j < count; j++) - { - ref var refNode = ref nodes[j]; - if (refNode.Value is ContainerRegistration containerRegistration) - seed.Add(type, refNode.Key, containerRegistration); - } - break; - - default: - throw new InvalidOperationException("Unknown type of registry"); + ref var entry = ref registry.Entries[i]; + if (entry.Registration is ContainerRegistration containerRegistration) + seed.Add(entry.Key.Type, entry.Key.Name, containerRegistration); } } @@ -291,34 +257,15 @@ namespace Unity if (null == container._registrations) return seed; + var registry = container._registry; + foreach (var type in types) { - var registry = container.Get(type); - if (null == registry?.Values) continue; - - switch (registry) + foreach (var i in container._metadata.Get(type)) { - case LinkedRegistry linkedRegistry: - for (var node = (LinkedNode)linkedRegistry; null != node; node = node.Next) - { - if (node.Value is ContainerRegistration containerRegistration && !string.IsNullOrEmpty(node.Key)) - seed.Add(type, node.Key, containerRegistration); - } - break; - - case HashRegistry hashRegistry: - var count = hashRegistry.Count; - var nodes = hashRegistry.Entries; - for (var j = 0; j < count; j++) - { - ref var refNode = ref nodes[j]; - if (refNode.Value is ContainerRegistration containerRegistration && !string.IsNullOrEmpty(refNode.Key)) - seed.Add(type, refNode.Key, containerRegistration); - } - break; - - default: - throw new InvalidOperationException("Unknown type of registry"); + ref var entry = ref registry.Entries[i]; + if (entry.Registration is ContainerRegistration containerRegistration && !string.IsNullOrEmpty(entry.Key.Name)) + seed.Add(entry.Key.Type, entry.Key.Name, containerRegistration); } } @@ -365,8 +312,36 @@ namespace Unity : GetOrAddGeneric(type, name, info.GetGenericTypeDefinition()); } + + private InternalRegistration CreateRegistration(ref NamedType key) + { + // TODO: Verify constructor + var registration = new InternalRegistration(key.Type, key.Name); + + if (key.Type.GetTypeInfo().IsGenericType) + { + var factory = (InternalRegistration)_get(key.Type.GetGenericTypeDefinition(), key.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(key.Type, registration); + return registration; + } + private IPolicySet CreateRegistration(Type type, string name) { + // TODO: Verify constructor var registration = new InternalRegistration(type, name); if (type.GetTypeInfo().IsGenericType) @@ -405,8 +380,8 @@ namespace Unity private IEnumerable AddOrReplaceRegistrations(IEnumerable interfaces, string name, ContainerRegistration registration) { NamedType key; - int count = 0; + int count = 0; key.Name = name; if (null != interfaces) @@ -418,16 +393,7 @@ namespace Unity // 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; - } + if (null != previous) yield return previous; count++; } @@ -443,20 +409,16 @@ namespace Unity // 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; - } + if (null != previous) yield return previous; } } + #endregion + + + #region Legacy + private IPolicySet AddOrUpdateLegacy(Type type, string name, InternalRegistration registration) { var collisions = 0; diff --git a/src/UnityContainer.Registry.cs b/src/UnityContainer.Registry.cs index d8c89622..f7ff0ed3 100644 --- a/src/UnityContainer.Registry.cs +++ b/src/UnityContainer.Registry.cs @@ -1,4 +1,4 @@ -using Unity.Policy; +using System.Diagnostics; using Unity.Registration; using Unity.Resolution; using Unity.Storage; @@ -7,6 +7,13 @@ namespace Unity { public partial class UnityContainer { + #region Constants + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private const int CollisionsCutPoint = 5; + + #endregion + #region Fields private Registry _registry; @@ -15,9 +22,9 @@ namespace Unity #endregion - #region + #region Registrations Manipulation - private IPolicySet InitAndAdd(ref NamedType key, InternalRegistration registration) + private InternalRegistration InitAndAdd(ref NamedType key, InternalRegistration registration) { lock (_syncRoot) { @@ -27,15 +34,16 @@ namespace Unity _metadata = new Metadata(); Register = AddOrReplace; + GetRegistration = GetOrAdd; } } return Register(ref key, registration); } - private IPolicySet AddOrReplace(ref NamedType key, InternalRegistration registration) + private InternalRegistration AddOrReplace(ref NamedType key, InternalRegistration registration) { - var hashCode = key.GetHashCode(); + var hashCode = key.GetHashCode() & 0x7FFFFFFF; var targetBucket = hashCode % _registry.Buckets.Length; var collisions = 0; @@ -51,24 +59,30 @@ namespace Unity continue; } - var existing = candidate.Reference; + // Replace registration + var existing = candidate.Registration; - candidate.Reference = registration; + candidate.Registration = registration; + candidate.Registration.AddRef(); return existing; } - if (_registry.RequireToGrow || ListToHashCutPoint < collisions) + // Expand if required + if (_registry.RequireToGrow || CollisionsCutPoint < collisions) { _registry = new Registry(_registry); targetBucket = hashCode % _registry.Buckets.Length; } + // Add registration ref var entry = ref _registry.Entries[_registry.Count]; entry.HashCode = hashCode; entry.Next = _registry.Buckets[targetBucket]; entry.Key = key; - entry.Reference = registration; + entry.Registration = registration; + entry.Registration.AddRef(); + var position = _registry.Count++; _registry.Buckets[targetBucket] = position; _metadata.Add(key.Type, position); @@ -77,6 +91,64 @@ namespace Unity } } + private InternalRegistration GetOrAdd(ref NamedType key) + { + var hashCode = key.GetHashCode() & 0x7FFFFFFF; + var targetBucket = hashCode % _registry.Buckets.Length; + var registry = _registry; + + // Check for existing without squaring the lock first + 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) + { + continue; + } + + // Found a registration + return candidate.Registration; + } + + // Nothing found so get the lock and add a new registration + + // Do the double-check lock to verify it was not yet added + lock (_syncRoot) + { + var collisions = 0; + + 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; + } + + return candidate.Registration; + } + + if (_registry.RequireToGrow || CollisionsCutPoint < collisions) + { + _registry = new Registry(_registry); + targetBucket = hashCode % _registrations.Buckets.Length; + } + + // Add registration + ref var entry = ref _registry.Entries[_registry.Count]; + entry.HashCode = hashCode; + entry.Next = _registry.Buckets[targetBucket]; + entry.Key = key; + entry.Registration = CreateRegistration(ref key); + entry.Registration.AddRef(); + _registry.Buckets[targetBucket] = _registry.Count++; + + return entry.Registration; + } + } #endregion }