Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
MemberReflectionOptimizer is now threadsafe
  • Loading branch information
abolibibelot committed Jul 6, 2010
1 parent 33aba6c commit 9df84d1
Showing 1 changed file with 102 additions and 57 deletions.
159 changes: 102 additions & 57 deletions source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs
Expand Up @@ -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.
Expand Down Expand Up @@ -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>
Expand All @@ -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");
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand Down

0 comments on commit 9df84d1

Please sign in to comment.