From 7ca151a3fa01b5cee229e5a873def24abbbb7759 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 21 Jan 2018 10:18:13 +0100 Subject: [PATCH] Use concurrent dictionaries --- src/Umbraco.Core/ObjectExtensions.cs | 105 ++++++++++++--------------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index e875f5942f..ed4e4aca6a 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -17,11 +18,11 @@ namespace Umbraco.Core public static class ObjectExtensions { // Cache the various type lookups - private static readonly Dictionary NullableGenericCache = new Dictionary(); - private static readonly Dictionary InputTypeConverterCache = new Dictionary(); - private static readonly Dictionary DestinationTypeConverterCache = new Dictionary(); - private static readonly Dictionary AssignableTypeCache = new Dictionary(); - private static readonly Dictionary BoolConvertCache = new Dictionary(); + private static readonly ConcurrentDictionary NullableGenericCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InputTypeConverterCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary DestinationTypeConverterCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary AssignableTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary BoolConvertCache = new ConcurrentDictionary(); private static readonly char[] NumberDecimalSeparatorsToNormalize = { '.', ',' }; private static readonly CustomBooleanTypeConverter CustomBooleanTypeConverter = new CustomBooleanTypeConverter(); @@ -72,7 +73,7 @@ namespace Umbraco.Core /// The public static Attempt TryConvertTo(this object input) { - var result = TryConvertTo(input, typeof(T)); + var result = TryConvertTo(input, typeof(T)); if (result.Success) return Attempt.Succeed((T) result.Result); @@ -101,7 +102,7 @@ namespace Umbraco.Core { return Attempt.Fail(); } - + try { if (input == null) @@ -162,8 +163,8 @@ namespace Umbraco.Core } } else - { - // target is not a generic type + { + // target is not a generic type if (input is string inputString) { @@ -288,7 +289,7 @@ namespace Umbraco.Core return Attempt.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2)); } - // TODO: Should we do the decimal trick for short, byte, unsigned? + // TODO: Should we do the decimal trick for short, byte, unsigned? if (target == typeof(bool)) { @@ -651,71 +652,62 @@ namespace Umbraco.Core var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized); } - - // gets a converter for source, that can convert to target, or null if none exists + + // gets a converter for source, that can convert to target, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TypeConverter GetCachedSourceTypeConverter(Type source, Type target) { var key = new CompositeTypeTypeKey(source, target); - if (InputTypeConverterCache.TryGetValue(key, out var cached)) - return cached; - - var converter = TypeDescriptor.GetConverter(source); - if (converter.CanConvertTo(target)) + return InputTypeConverterCache.GetOrAdd(key, k => { - InputTypeConverterCache[key] = converter; - return converter; - } + var ksource = k.Type1; + var ktarget = k.Type2; - InputTypeConverterCache[key] = null; - return null; + var converter = TypeDescriptor.GetConverter(ksource); + return converter.CanConvertTo(ktarget) ? converter : null; + }); } - + // gets a converter for target, that can convert from source, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TypeConverter GetCachedTargetTypeConverter(Type source, Type target) { - var key = new CompositeTypeTypeKey(source, target); - if (DestinationTypeConverterCache.TryGetValue(key, out var cached)) - return cached; - - var converter = TypeDescriptor.GetConverter(target); - if (converter.CanConvertFrom(source)) + var key = new CompositeTypeTypeKey(source, target); + return DestinationTypeConverterCache.GetOrAdd(key, k => { - DestinationTypeConverterCache[key] = converter; - return converter; - } + var ksource = k.Type1; + var ktarget = k.Type2; - DestinationTypeConverterCache[key] = null; - return null; + var converter = TypeDescriptor.GetConverter(ktarget); + return converter.CanConvertFrom(ksource) ? converter : null; + }); } - + // gets the underlying type of a nullable type, or null if the type is not nullable [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Type GetCachedGenericNullableType(Type type) { - if (NullableGenericCache.TryGetValue(type, out var cached)) - return cached; - - if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - var underlying = Nullable.GetUnderlyingType(type); - NullableGenericCache[type] = underlying; - return underlying; - } - - NullableGenericCache[type] = null; - return null; - + return NullableGenericCache.GetOrAdd(type, t + => t.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(type) : null; } - + // gets an IConvertible from source to target type, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] private static IConvertible GetCachedAssignableConvertibleResult(object input, Type source, Type target) { var key = new CompositeTypeTypeKey(source, target); + return AssignableTypeCache.GetOrAdd(key, k => + { + var ksource = k.Type1; + var ktarget = k.Type2; + + // FIXME the value we are caching does not depend on the key - WHAT IS THIS? + return ktarget.IsAssignableFrom(ksource) && input is IConvertible convertible ? convertible : null; + }); + + /* if (AssignableTypeCache.TryGetValue(key, out var cached)) - return cached; + return cached; if (target.IsAssignableFrom(source) && input is IConvertible convertible) { @@ -724,19 +716,16 @@ namespace Umbraco.Core } AssignableTypeCache[key] = null; - return null; + return null; + */ } - + // determines whether a type can be converted to boolean [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool GetCanConvertToBooleanResult(Type type) { - if (BoolConvertCache.TryGetValue(type, out var cached)) - return cached; - - var canConvert = CustomBooleanTypeConverter.CanConvertFrom(type); - BoolConvertCache[type] = canConvert; - return canConvert; + return BoolConvertCache.GetOrAdd(type, t + => CustomBooleanTypeConverter.CanConvertFrom(t)); } } } \ No newline at end of file