ソースを参照

Thread safe caching of global data

Brad Robinson 11 年 前
コミット
2c89f4bd0d
1 ファイル変更135 行追加67 行削除
  1. 135 67
      PetaJson.cs

+ 135 - 67
PetaJson.cs

@@ -15,6 +15,7 @@ using System.IO;
 using System.Reflection;
 using System.Globalization;
 using System.Collections;
+using System.Threading;
 #if PETAJSON_DYNAMIC
 using System.Dynamic;
 #endif
@@ -205,7 +206,7 @@ namespace PetaJson
         // Register a parser for a specified type
         public static void RegisterParser(Type type, Func<IJsonReader, Type, object> parser)
         {
-            Internal.Reader._parsers[type] = parser;
+            Internal.Reader._parsers.Set(type, parser);
         }
 
         // Register a typed parser
@@ -229,7 +230,7 @@ namespace PetaJson
         // Register an into parser
         public static void RegisterIntoParser(Type type, Action<IJsonReader, object> parser)
         {
-            Internal.Reader._intoParsers[type] = parser;
+            Internal.Reader._intoParsers.Set(type, parser);
         }
 
         // Register an into parser
@@ -243,7 +244,7 @@ namespace PetaJson
         // instance and which point it will switch to serialization using reflection
         public static void RegisterTypeFactory(Type type, Func<IJsonReader, string, object> factory)
         {
-            Internal.Reader._typeFactories[type] = factory;
+            Internal.Reader._typeFactories.Set(type, factory);
         }
 
         // Register a callback to provide a formatter for a newly encountered type
@@ -460,25 +461,25 @@ namespace PetaJson
                 };
 
                 // Default type handlers
-                _parsers.Add(typeof(string), simpleConverter);
-                _parsers.Add(typeof(char), simpleConverter);
-                _parsers.Add(typeof(bool), simpleConverter);
-                _parsers.Add(typeof(byte), simpleConverter);
-                _parsers.Add(typeof(sbyte), simpleConverter);
-                _parsers.Add(typeof(short), simpleConverter);
-                _parsers.Add(typeof(ushort), simpleConverter);
-                _parsers.Add(typeof(int), simpleConverter);
-                _parsers.Add(typeof(uint), simpleConverter);
-                _parsers.Add(typeof(long), simpleConverter);
-                _parsers.Add(typeof(ulong), simpleConverter);
-                _parsers.Add(typeof(decimal), simpleConverter);
-                _parsers.Add(typeof(float), simpleConverter);
-                _parsers.Add(typeof(double), simpleConverter);
-                _parsers.Add(typeof(DateTime), (reader, type) =>
+                _parsers.Set(typeof(string), simpleConverter);
+                _parsers.Set(typeof(char), simpleConverter);
+                _parsers.Set(typeof(bool), simpleConverter);
+                _parsers.Set(typeof(byte), simpleConverter);
+                _parsers.Set(typeof(sbyte), simpleConverter);
+                _parsers.Set(typeof(short), simpleConverter);
+                _parsers.Set(typeof(ushort), simpleConverter);
+                _parsers.Set(typeof(int), simpleConverter);
+                _parsers.Set(typeof(uint), simpleConverter);
+                _parsers.Set(typeof(long), simpleConverter);
+                _parsers.Set(typeof(ulong), simpleConverter);
+                _parsers.Set(typeof(decimal), simpleConverter);
+                _parsers.Set(typeof(float), simpleConverter);
+                _parsers.Set(typeof(double), simpleConverter);
+                _parsers.Set(typeof(DateTime), (reader, type) =>
                 {
                     return reader.ReadLiteral(literal => Utils.FromUnixMilliseconds((long)Convert.ChangeType(literal, typeof(long), CultureInfo.InvariantCulture)));
                 });
-                _parsers.Add(typeof(byte[]), (reader, type) =>
+                _parsers.Set(typeof(byte[]), (reader, type) =>
                 {
                     return reader.ReadLiteral(literal => Convert.FromBase64String((string)Convert.ChangeType(literal, typeof(string), CultureInfo.InvariantCulture)));
                 });
@@ -663,10 +664,9 @@ namespace PetaJson
                 // Call value type resolver
                 if (type.IsValueType)
                 {
-                    var tp = _parserResolver(type);
+                    var tp = _parsers.Get(type, () => _parserResolver(type));
                     if (tp != null)
                     {
-                        _parsers[type] = tp;
                         return tp(this, type);
                     }
                 }
@@ -761,10 +761,9 @@ namespace PetaJson
                 }
 
                 // Try to resolve a parser
-                var intoParser = _intoParserResolver(type);
+                var intoParser = _intoParsers.Get(type, () => _intoParserResolver(type));
                 if (intoParser != null)
                 {
-                    _intoParsers[type] = intoParser;
                     intoParser(this, into);
                     return;
                 }
@@ -878,9 +877,9 @@ namespace PetaJson
             // Yikes!
             public static Func<Type, Action<IJsonReader, object>> _intoParserResolver;
             public static Func<Type, Func<IJsonReader, Type, object>> _parserResolver;
-            public static Dictionary<Type, Func<IJsonReader, Type, object>> _parsers = new Dictionary<Type, Func<IJsonReader, Type, object>>();
-            public static Dictionary<Type, Action<IJsonReader, object>> _intoParsers = new Dictionary<Type, Action<IJsonReader, object>>();
-            public static Dictionary<Type, Func<IJsonReader, string, object>> _typeFactories = new Dictionary<Type, Func<IJsonReader, string, object>>();
+            public static ThreadSafeCache<Type, Func<IJsonReader, Type, object>> _parsers = new ThreadSafeCache<Type, Func<IJsonReader, Type, object>>();
+            public static ThreadSafeCache<Type, Action<IJsonReader, object>> _intoParsers = new ThreadSafeCache<Type, Action<IJsonReader, object>>();
+            public static ThreadSafeCache<Type, Func<IJsonReader, string, object>> _typeFactories = new ThreadSafeCache<Type, Func<IJsonReader, string, object>>();
         }
 
         public class Writer : IJsonWriter
@@ -1221,7 +1220,7 @@ namespace PetaJson
             public List<JsonMemberInfo> Members;
 
             // Cache of these ReflectionInfos's
-            static Dictionary<Type, ReflectionInfo> _cache = new Dictionary<Type, ReflectionInfo>();
+            static ThreadSafeCache<Type, ReflectionInfo> _cache = new ThreadSafeCache<Type, ReflectionInfo>();
 
             // Write one of these types
             public void Write(IJsonWriter w, object val)
@@ -1320,55 +1319,52 @@ namespace PetaJson
             // Get the reflection info for a specified type
             public static ReflectionInfo GetReflectionInfo(Type type)
             {
-                // Already created?
-                ReflectionInfo existing;
-                if (_cache.TryGetValue(type, out existing))
-                    return existing;
-
-                // Does type have a [Json] attribute
-                bool typeMarked = type.GetCustomAttributes(typeof(JsonAttribute), true).OfType<JsonAttribute>().Any();
+                // Check cache
+                return _cache.Get(type, () =>
+                {
+                    // Does type have a [Json] attribute
+                    bool typeMarked = type.GetCustomAttributes(typeof(JsonAttribute), true).OfType<JsonAttribute>().Any();
 
-                // Do any members have a [Json] attribute
-                bool anyFieldsMarked = Utils.GetAllFieldsAndProperties(type).Any(x => x.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().Any());
+                    // Do any members have a [Json] attribute
+                    bool anyFieldsMarked = Utils.GetAllFieldsAndProperties(type).Any(x => x.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().Any());
 
-                // Should we serialize all public methods?
-                bool serializeAllPublics = typeMarked || !anyFieldsMarked;
+                    // Should we serialize all public methods?
+                    bool serializeAllPublics = typeMarked || !anyFieldsMarked;
 
-                // Build 
-                var ri = CreateReflectionInfo(type, mi =>
-                {
-                    // Explicitly excluded?
-                    if (mi.GetCustomAttributes(typeof(JsonExcludeAttribute), false).OfType<JsonExcludeAttribute>().Any())
-                        return null;
-
-                    // Get attributes
-                    var attr = mi.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().FirstOrDefault();
-                    if (attr != null)
+                    // Build 
+                    var ri = CreateReflectionInfo(type, mi =>
                     {
-                        return new JsonMemberInfo()
+                        // Explicitly excluded?
+                        if (mi.GetCustomAttributes(typeof(JsonExcludeAttribute), false).OfType<JsonExcludeAttribute>().Any())
+                            return null;
+
+                        // Get attributes
+                        var attr = mi.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().FirstOrDefault();
+                        if (attr != null)
                         {
-                            Member = mi,
-                            JsonKey = attr.Key ?? mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
-                            KeepInstance = attr.KeepInstance,
-                        };
-                    }
+                            return new JsonMemberInfo()
+                            {
+                                Member = mi,
+                                JsonKey = attr.Key ?? mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
+                                KeepInstance = attr.KeepInstance,
+                            };
+                        }
 
-                    // Serialize all publics?
-                    if (serializeAllPublics && Utils.IsPublic(mi))
-                    {
-                        return new JsonMemberInfo()
+                        // Serialize all publics?
+                        if (serializeAllPublics && Utils.IsPublic(mi))
                         {
-                            Member = mi,
-                            JsonKey = mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
-                        };
-                    }
+                            return new JsonMemberInfo()
+                            {
+                                Member = mi,
+                                JsonKey = mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
+                            };
+                        }
 
-                    return null;
-                });
+                        return null;
+                    });
 
-                // Cache it
-                _cache[type] = ri;
-                return ri;
+                    return ri;
+                });
             }
 
             public static ReflectionInfo CreateReflectionInfo(Type type, Func<MemberInfo, JsonMemberInfo> callback)
@@ -1392,6 +1388,78 @@ namespace PetaJson
             }
         }
 
+        public class ThreadSafeCache<TKey, TValue>
+        {
+            public ThreadSafeCache()
+            {
+
+            }
+
+            public TValue Get(TKey key, Func<TValue> createIt)
+            {
+                // Check if already exists
+                _lock.EnterReadLock();
+                try
+                {
+                    TValue val;
+                    if (_map.TryGetValue(key, out val))
+                        return val;
+                }
+                finally
+                {
+                    _lock.ExitReadLock();
+                }
+
+                // Nope, take lock and try again
+                _lock.EnterWriteLock();
+                try
+                {
+                    // Check again before creating it
+                    TValue val;
+                    if (!_map.TryGetValue(key, out val))
+                    {
+                        // Now create it
+                        val = createIt();
+                        _map[key] = val;
+                    }
+                    return val;
+                }
+                finally
+                {
+                    _lock.ExitWriteLock();
+                }
+            }
+
+            public bool TryGetValue(TKey key, out TValue val)
+            {
+                _lock.EnterReadLock();
+                try
+                {
+                    return _map.TryGetValue(key, out val);
+                }
+                finally
+                {
+                    _lock.ExitReadLock();
+                }
+            }
+
+            public void Set(TKey key, TValue value)
+            {
+                _lock.EnterWriteLock();
+                try
+                {
+                    _map[key] = value;
+                }
+                finally
+                {
+                    _lock.ExitWriteLock();
+                }
+            }
+
+            Dictionary<TKey, TValue> _map = new Dictionary<TKey,TValue>();
+            ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
+        }
+
         internal static class Utils
         {
             // Get all fields and properties of a type