From 9df84d178fb54ffc8d514612ea2e2845cfb20886 Mon Sep 17 00:00:00 2001 From: Yann Schwartz Date: Tue, 6 Jul 2010 19:35:40 +0200 Subject: [PATCH] MemberReflectionOptimizer is now threadsafe --- .../Mapping/Util/MemberReflectionOptimizer.cs | 159 +++++++++++------- 1 file changed, 102 insertions(+), 57 deletions(-) diff --git a/source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs b/source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs index b28a6223..8a5202ec 100644 --- a/source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs +++ b/source/MongoDB/Configuration/Mapping/Util/MemberReflectionOptimizer.cs @@ -13,6 +13,7 @@ public static class MemberReflectionOptimizer { private static readonly Dictionary> GetterCache = new Dictionary>(); private static readonly Dictionary> SetterCache = new Dictionary>(); + private static readonly object SyncObject = new object(); /// /// 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>( - Expression.Convert(member, typeof(object)), - instanceParameter); - - var result = lambda.Compile(); - GetterCache[key] = result; - return result; + Func 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>( + Expression.Convert(member, typeof (object)), + instanceParameter); + + getter = lambda.Compile(); + + lock(SyncObject) + { + GetterCache[key] = getter; + } + + return getter; } /// @@ -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 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; } /// @@ -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)method.CreateDelegate(typeof(Action)); - SetterCache[key] = result; - return result; + + Action 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) method.CreateDelegate(typeof (Action)); + + lock (SyncObject) + { + SetterCache[key] = setter; + } + + return setter; } /// @@ -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 setter; - var instanceParameter = Expression.Parameter(typeof(object), "target"); - var valueParameter = Expression.Parameter(typeof(object), "value"); - - var lambda = Expression.Lambda>( - 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>( + 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; } ///