Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

MemberReflectionOptimizer is now threadsafe

  • Loading branch information...
commit 9df84d178fb54ffc8d514612ea2e2845cfb20886 1 parent 33aba6c
@abolibibelot abolibibelot authored
Showing with 102 additions and 57 deletions.
  1. +102 −57 source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs
View
159 source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs
@@ -13,6 +13,7 @@ public static class MemberReflectionOptimizer
{
private static readonly Dictionary<string, Func<object, object>> GetterCache = new Dictionary<string, Func<object, object>>();
private static readonly Dictionary<string, Action<object, object>> SetterCache = new Dictionary<string, Action<object, object>>();
+ private static readonly object SyncObject = new object();
/// <summary>
/// Gets the getter.
@@ -46,20 +47,32 @@ public static class MemberReflectionOptimizer
throw new ArgumentNullException("fieldInfo");
var key = CreateKey(fieldInfo);
- if(GetterCache.ContainsKey(key))
- return GetterCache[key];
- var instanceParameter = Expression.Parameter(typeof(object), "target");
-
- var member = Expression.Field(Expression.Convert(instanceParameter, fieldInfo.DeclaringType), fieldInfo);
-
- var lambda = Expression.Lambda<Func<object, object>>(
- Expression.Convert(member, typeof(object)),
- instanceParameter);
-
- var result = lambda.Compile();
- GetterCache[key] = result;
- return result;
+ Func<object, object> getter;
+ lock (SyncObject)
+ {
+ if (GetterCache.TryGetValue(key, out getter))
+ return getter;
+ }
+ //We release the lock here, so the relatively time consuming compiling
+ //does not imply contention. The price to pay is potential multiple compilations
+ //of the same expression...
+ var instanceParameter = Expression.Parameter(typeof (object), "target");
+
+ var member = Expression.Field(Expression.Convert(instanceParameter, fieldInfo.DeclaringType), fieldInfo);
+
+ var lambda = Expression.Lambda<Func<object, object>>(
+ Expression.Convert(member, typeof (object)),
+ instanceParameter);
+
+ getter = lambda.Compile();
+
+ lock(SyncObject)
+ {
+ GetterCache[key] = getter;
+ }
+
+ return getter;
}
/// <summary>
@@ -73,10 +86,16 @@ public static class MemberReflectionOptimizer
throw new ArgumentNullException("propertyInfo");
var key = CreateKey(propertyInfo);
- if(GetterCache.ContainsKey(key))
- return GetterCache[key];
- if(!propertyInfo.CanRead)
+ Func<object, object> getter;
+
+ lock (SyncObject)
+ {
+ if (GetterCache.TryGetValue(key, out getter))
+ return getter;
+ }
+
+ if(!propertyInfo.CanRead)
throw new InvalidOperationException("Cannot create a getter for a writeonly property.");
var instanceParameter = Expression.Parameter(typeof(object), "target");
@@ -87,9 +106,13 @@ public static class MemberReflectionOptimizer
Expression.Convert(member, typeof(object)),
instanceParameter);
- var result = lambda.Compile();
- GetterCache[key] = result;
- return result;
+ getter = lambda.Compile();
+
+ lock (SyncObject)
+ {
+ GetterCache[key] = getter;
+ }
+ return getter;
}
/// <summary>
@@ -124,26 +147,37 @@ public static class MemberReflectionOptimizer
throw new ArgumentNullException("fieldInfo");
var key = CreateKey(fieldInfo);
- if(SetterCache.ContainsKey(key))
- return SetterCache[key];
-
- if(fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
- throw new InvalidOperationException("Cannot create a setter for a readonly field.");
-
- var sourceType = fieldInfo.DeclaringType;
- var method = new DynamicMethod("Set" + fieldInfo.Name, null, new[] {typeof(object), typeof(object)}, true);
- var gen = method.GetILGenerator();
-
- gen.Emit(OpCodes.Ldarg_0);
- gen.Emit(OpCodes.Castclass, sourceType);
- gen.Emit(OpCodes.Ldarg_1);
- gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
- gen.Emit(OpCodes.Stfld, fieldInfo);
- gen.Emit(OpCodes.Ret);
-
- var result = (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
- SetterCache[key] = result;
- return result;
+
+ Action<object, object> setter;
+
+ lock (SyncObject)
+ {
+ if (SetterCache.TryGetValue(key, out setter))
+ return setter;
+ }
+
+ if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
+ throw new InvalidOperationException("Cannot create a setter for a readonly field.");
+
+ var sourceType = fieldInfo.DeclaringType;
+ var method = new DynamicMethod("Set" + fieldInfo.Name, null, new[] {typeof (object), typeof (object)}, true);
+ var gen = method.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, sourceType);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
+ gen.Emit(OpCodes.Stfld, fieldInfo);
+ gen.Emit(OpCodes.Ret);
+
+ setter = (Action<object, object>) method.CreateDelegate(typeof (Action<object, object>));
+
+ lock (SyncObject)
+ {
+ SetterCache[key] = setter;
+ }
+
+ return setter;
}
/// <summary>
@@ -157,26 +191,37 @@ public static class MemberReflectionOptimizer
throw new ArgumentNullException("propertyInfo");
var key = CreateKey(propertyInfo);
- if(SetterCache.ContainsKey(key))
- return SetterCache[key];
- if(!propertyInfo.CanWrite)
- throw new InvalidOperationException("Cannot create a setter for a readonly property.");
+ Action<object, object> setter;
- var instanceParameter = Expression.Parameter(typeof(object), "target");
- var valueParameter = Expression.Parameter(typeof(object), "value");
-
- var lambda = Expression.Lambda<Action<object, object>>(
- Expression.Call(
- Expression.Convert(instanceParameter, propertyInfo.DeclaringType),
- propertyInfo.GetSetMethod(true),
- Expression.Convert(valueParameter, propertyInfo.PropertyType)),
- instanceParameter,
- valueParameter);
-
- var result = lambda.Compile();
- SetterCache[key] = result;
- return result;
+ lock (SyncObject)
+ {
+ if (SetterCache.TryGetValue(key, out setter))
+ return setter;
+ }
+
+ if (!propertyInfo.CanWrite)
+ throw new InvalidOperationException("Cannot create a setter for a readonly property.");
+
+ var instanceParameter = Expression.Parameter(typeof (object), "target");
+ var valueParameter = Expression.Parameter(typeof (object), "value");
+
+ var lambda = Expression.Lambda<Action<object, object>>(
+ Expression.Call(
+ Expression.Convert(instanceParameter, propertyInfo.DeclaringType),
+ propertyInfo.GetSetMethod(true),
+ Expression.Convert(valueParameter, propertyInfo.PropertyType)),
+ instanceParameter,
+ valueParameter);
+
+ setter = lambda.Compile();
+
+ lock (SyncObject)
+ {
+ SetterCache[key] = setter;
+ }
+
+ return setter;
}
/// <summary>
Please sign in to comment.
Something went wrong with that request. Please try again.