diff --git a/src/Castle.Core/Components.DictionaryAdapter/DictionaryAdapterFactory.cs b/src/Castle.Core/Components.DictionaryAdapter/DictionaryAdapterFactory.cs index a2cb08a60d..5088fce073 100644 --- a/src/Castle.Core/Components.DictionaryAdapter/DictionaryAdapterFactory.cs +++ b/src/Castle.Core/Components.DictionaryAdapter/DictionaryAdapterFactory.cs @@ -16,6 +16,7 @@ namespace Castle.Components.DictionaryAdapter { using System; using System.Collections; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -34,8 +35,8 @@ namespace Castle.Components.DictionaryAdapter /// public class DictionaryAdapterFactory : IDictionaryAdapterFactory { - private readonly SynchronizedDictionary interfaceToMeta = - new SynchronizedDictionary(); + private readonly ConcurrentDictionary interfaceToMeta = + new ConcurrentDictionary(); #region IDictionaryAdapterFactory diff --git a/src/Castle.Core/Core/Internal/SynchronizedDictionary.cs b/src/Castle.Core/Core/Internal/SynchronizedDictionary.cs deleted file mode 100644 index 4f354f3222..0000000000 --- a/src/Castle.Core/Core/Internal/SynchronizedDictionary.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2004-2021 Castle Project - http://www.castleproject.org/ -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace Castle.Core.Internal -{ - using System; - using System.Collections.Generic; - using System.Threading; - - internal sealed class SynchronizedDictionary : IDisposable - { - private Dictionary items; - private ReaderWriterLockSlim itemsLock; - - public SynchronizedDictionary() - { - items = new Dictionary(); - itemsLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - } - - public void AddOrUpdateWithoutTakingLock(TKey key, TValue value) - { - items[key] = value; - } - - public void Dispose() - { - itemsLock.Dispose(); - } - - public TValue GetOrAdd(TKey key, Func valueFactory) - { - TValue value; - - itemsLock.EnterReadLock(); - try - { - if (items.TryGetValue(key, out value)) - { - return value; - } - } - finally - { - itemsLock.ExitReadLock(); - } - - itemsLock.EnterUpgradeableReadLock(); - try - { - if (items.TryGetValue(key, out value)) - { - return value; - } - else - { - value = valueFactory.Invoke(key); - - itemsLock.EnterWriteLock(); - try - { - items.Add(key, value); - return value; - } - finally - { - itemsLock.ExitWriteLock(); - } - } - } - finally - { - itemsLock.ExitUpgradeableReadLock(); - } - } - - public TValue GetOrAddWithoutTakingLock(TKey key, Func valueFactory) - { - TValue value; - - if (items.TryGetValue(key, out value)) - { - return value; - } - else - { - value = valueFactory.Invoke(key); - items.Add(key, value); - return value; - } - } - - public void ForEach(Action action) - { - itemsLock.EnterReadLock(); - try - { - foreach (var item in items) - { - action.Invoke(item.Key, item.Value); - } - } - finally - { - itemsLock.ExitReadLock(); - } - } - } -} diff --git a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyTargetContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/ClassProxyTargetContributor.cs index 191cfe1a86..dc12131af3 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyTargetContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/ClassProxyTargetContributor.cs @@ -176,7 +176,7 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class) GetCacheKeyTypes(method), null); - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => + return scope.GetOrAddFromCache(key, _ => new DelegateTypeGenerator(method, targetType) .Generate(@class, namingScope) .BuildType()); diff --git a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyWithTargetTargetContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/ClassProxyWithTargetTargetContributor.cs index d834373c3b..9f54a3c99e 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyWithTargetTargetContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/ClassProxyWithTargetTargetContributor.cs @@ -121,7 +121,7 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class) GetCacheKeyTypes(method), null); - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => + return scope.GetOrAddFromCache(key, _ => new DelegateTypeGenerator(method, targetType) .Generate(@class, namingScope) .BuildType()); @@ -136,7 +136,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter @class) // no locking required as we're already within a lock - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => BuildInvocationType(method, @class)); + return scope.GetOrAddFromCache(key, _ => BuildInvocationType(method, @class)); } private MethodGenerator IndirectlyCalledMethodGenerator(MetaMethod method, ClassEmitter proxy, diff --git a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyTargetContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyTargetContributor.cs index deb0cde955..aad6d72ec4 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyTargetContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyTargetContributor.cs @@ -88,7 +88,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter @class) // no locking required as we're already within a lock - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => + return scope.GetOrAddFromCache(key, _ => new CompositionInvocationTypeGenerator(method.Method.DeclaringType, method, method.Method, diff --git a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs index d3eca51ad7..9f10f55a6b 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs @@ -95,7 +95,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter emitter) // no locking required as we're already within a lock - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => + return scope.GetOrAddFromCache(key, _ => new CompositionInvocationTypeGenerator(methodInfo.DeclaringType, method, methodInfo, diff --git a/src/Castle.Core/DynamicProxy/Contributors/MixinContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/MixinContributor.cs index 9f63b269bd..3607ec1cfe 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/MixinContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/MixinContributor.cs @@ -140,7 +140,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter emitter) // no locking required as we're already within a lock - return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => + return scope.GetOrAddFromCache(key, _ => new CompositionInvocationTypeGenerator(method.Method.DeclaringType, method, method.Method, diff --git a/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs b/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs index 703e4aafb5..6900e65c1c 100644 --- a/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs +++ b/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs @@ -152,7 +152,7 @@ private void AssertValidTypeForTarget(Type type, Type target, string paramName) } } - private void AssertValidTypes(IEnumerable? targetTypes, string paramName) + private void AssertValidTypes(Type[]? targetTypes, string paramName) { if (targetTypes != null) { diff --git a/src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs index e6408fdaf3..824b46d168 100644 --- a/src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs @@ -271,6 +271,11 @@ private void EnsureValidBaseType(Type type) "Base type for proxy is null reference. Please set it to System.Object or some other valid type."); } + if (typeof(object).Equals(type)) + { + return; // Skip GetConstructor reflection for System.Object + } + if (!type.IsClass) { ThrowInvalidBaseType(type, "it is not a class type"); diff --git a/src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs index 1dc5806b82..842bb4d817 100644 --- a/src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs @@ -40,7 +40,7 @@ internal abstract class BaseProxyGenerator protected readonly Type[] interfaces; private readonly ModuleScope scope; private ILogger logger = NullLogger.Instance; - private ProxyGenerationOptions proxyGenerationOptions; + private readonly ProxyGenerationOptions proxyGenerationOptions; protected BaseProxyGenerator(ModuleScope scope, Type targetType, Type[] interfaces, ProxyGenerationOptions proxyGenerationOptions) { @@ -74,9 +74,11 @@ public Type GetProxyType() { bool notFoundInTypeCache = false; - var proxyType = Scope.TypeCache.GetOrAdd(GetCacheKey(), cacheKey => + var cacheKey = GetCacheKey(); + var proxyType = Scope.GetOrAddFromCache(cacheKey, key => { notFoundInTypeCache = true; + Logger.DebugFormat("No cached proxy type was found for target type {0}.", targetType.FullName); EnsureOptionsOverrideEqualsAndGetHashCode(); diff --git a/src/Castle.Core/DynamicProxy/Generators/CacheKey.cs b/src/Castle.Core/DynamicProxy/Generators/CacheKey.cs index d0a98b8f62..5f619ee30f 100644 --- a/src/Castle.Core/DynamicProxy/Generators/CacheKey.cs +++ b/src/Castle.Core/DynamicProxy/Generators/CacheKey.cs @@ -20,7 +20,7 @@ namespace Castle.DynamicProxy.Generators #if FEATURE_SERIALIZATION [Serializable] #endif - internal class CacheKey + internal struct CacheKey : IEquatable { private readonly MemberInfo target; private readonly Type[] interfaces; @@ -73,37 +73,31 @@ public override int GetHashCode() public override bool Equals(object obj) { - if (this == obj) - { - return true; - } - - var cacheKey = obj as CacheKey; - if (cacheKey == null) - { - return false; - } + return obj is CacheKey other && Equals(other); + } - if (!Equals(type, cacheKey.type)) + public bool Equals(CacheKey other) + { + if (!Equals(type, other.type)) { return false; } - if (!Equals(target, cacheKey.target)) + if (!Equals(target, other.target)) { return false; } - if (interfaces.Length != cacheKey.interfaces.Length) + if (interfaces.Length != other.interfaces.Length) { return false; } for (var i = 0; i < interfaces.Length; i++) { - if (!Equals(interfaces[i], cacheKey.interfaces[i])) + if (!Equals(interfaces[i], other.interfaces[i])) { return false; } } - if (!Equals(options, cacheKey.options)) + if (!Equals(options, other.options)) { return false; } diff --git a/src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs b/src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs index e82c7c5e89..0fb157e8c6 100644 --- a/src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs +++ b/src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs @@ -15,18 +15,16 @@ namespace Castle.DynamicProxy.Internal { using System; - using System.Collections.Generic; + using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection; - using System.Threading; - using Castle.Core.Internal; using Castle.DynamicProxy.Generators; internal static class InvocationHelper { - private static readonly SynchronizedDictionary cache = - new SynchronizedDictionary(); + private static readonly ConcurrentDictionary cache = + new ConcurrentDictionary(); public static MethodInfo GetMethodOnObject(object target, MethodInfo proxiedMethod) { diff --git a/src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs b/src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs index 230aeae51e..ece9c58567 100644 --- a/src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs +++ b/src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs @@ -130,15 +130,14 @@ internal static MethodInfo[] GetAllInstanceMethods(this Type type) /// internal static Type[] GetAllInterfaces(params Type[]? types) { - if (types == null) + if (types == null || types.Length == 0) { return Type.EmptyTypes; } var interfaces = new HashSet(); - for (var index = 0; index < types.Length; index++) + foreach (var type in types) { - var type = types[index]; if (type == null) { continue; @@ -153,9 +152,8 @@ internal static Type[] GetAllInterfaces(params Type[]? types) } var innerInterfaces = type.GetInterfaces(); - for (var i = 0; i < innerInterfaces.Length; i++) + foreach (var @interface in innerInterfaces) { - var @interface = innerInterfaces[i]; interfaces.Add(@interface); } } @@ -168,7 +166,6 @@ public static Type[] GetAllInterfaces(this Type type) // NOTE: also used by Win return GetAllInterfaces(new[] { type }); } - public static Type? GetTypeOrNull(object? target) { if (target == null) @@ -307,6 +304,9 @@ internal static bool IsDelegateType(this Type type) private static Type[] Sort(ICollection types) { + if (types.Count == 0) + return Type.EmptyTypes; + var array = new Type[types.Count]; types.CopyTo(array, 0); //NOTE: is there a better, stable way to sort Types. We will need to revise this once we allow open generics diff --git a/src/Castle.Core/DynamicProxy/ModuleScope.cs b/src/Castle.Core/DynamicProxy/ModuleScope.cs index 908b91a445..209d2221aa 100644 --- a/src/Castle.Core/DynamicProxy/ModuleScope.cs +++ b/src/Castle.Core/DynamicProxy/ModuleScope.cs @@ -18,13 +18,12 @@ namespace Castle.DynamicProxy { using System; using System.Collections.Generic; - using System.Diagnostics; + using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Resources; - using System.Threading; using Castle.Core.Internal; using Castle.DynamicProxy.Generators; @@ -54,7 +53,7 @@ public class ModuleScope private readonly string weakModulePath; // Keeps track of generated types - private readonly SynchronizedDictionary typeCache = new SynchronizedDictionary(); + private readonly ConcurrentDictionary typeCache = new ConcurrentDictionary(); // Used to lock the module builder creation private readonly object moduleLocker = new object(); @@ -148,7 +147,23 @@ internal INamingScope NamingScope get { return namingScope; } } - internal SynchronizedDictionary TypeCache => typeCache; + internal Type GetOrAddFromCache(CacheKey key, Func factory) + { + if (typeCache.TryGetValue(key, out var type)) + return type; + + lock (typeCache) + { + if (typeCache.TryGetValue(key, out type)) + return type; + + type = factory(key); + if (typeCache.TryAdd(key, type)) + return type; + + return typeCache[key]; + } + } /// /// Gets the key pair used to sign the strong-named assembly generated by this . @@ -504,15 +519,13 @@ private void AddCacheMappings(AssemblyBuilder builder) { var mappings = new Dictionary(); - typeCache.ForEach((key, value) => + foreach (var type in typeCache) { - // NOTE: using == returns invalid results. - // we need to use Equals here for it to work properly - if (builder.Equals(value.Assembly)) + if (builder.Equals(type.Value.Assembly)) { - mappings.Add(key, value.FullName); + mappings.Add(type.Key, type.Value.FullName); } - }); + } CacheMappingsAttribute.ApplyTo(builder, mappings); } @@ -551,7 +564,7 @@ public void LoadAssemblyIntoCache(Assembly assembly) if (loadedType != null) { - typeCache.AddOrUpdateWithoutTakingLock(mapping.Key, loadedType); + typeCache[mapping.Key] = loadedType; } } } diff --git a/src/Castle.Core/DynamicProxy/ProxyUtil.cs b/src/Castle.Core/DynamicProxy/ProxyUtil.cs index 79bd611769..98d4ef3053 100644 --- a/src/Castle.Core/DynamicProxy/ProxyUtil.cs +++ b/src/Castle.Core/DynamicProxy/ProxyUtil.cs @@ -17,20 +17,18 @@ namespace Castle.DynamicProxy { using System; - using System.Collections.Generic; + using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; - using System.Threading; - using Castle.Core.Internal; using Castle.DynamicProxy.Generators; using Castle.DynamicProxy.Internal; public static class ProxyUtil { - private static readonly SynchronizedDictionary internalsVisibleToDynamicProxy = new SynchronizedDictionary(); + private static readonly ConcurrentDictionary internalsVisibleToDynamicProxy = new ConcurrentDictionary(); /// /// Creates a delegate of the specified type to a suitable `Invoke` method