Merge branch 'thread-safe-store-perf' of https://github.com/TylerBrinkley/Newtonsoft.Json into TylerBrinkley-thread-safe-store-perf

This commit is contained in:
James Newton-King
2017-06-24 22:10:18 +12:00
5 changed files with 95 additions and 92 deletions
+3 -3
View File
@@ -34,11 +34,11 @@
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net45'">
<AssemblyTitle>Json.NET</AssemblyTitle>
<DefineConstants>HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CAS;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_IMMUTABLE_COLLECTIONS;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_EMIT;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;$(AdditionalConstants)</DefineConstants>
<DefineConstants>HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CAS;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_IMMUTABLE_COLLECTIONS;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_EMIT;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;$(AdditionalConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net40'">
<AssemblyTitle>Json.NET .NET 4.0</AssemblyTitle>
<DefineConstants>NET40;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CAS;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_REFLECTION_EMIT;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;$(AdditionalConstants)</DefineConstants>
<DefineConstants>NET40;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CAS;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_REFLECTION_EMIT;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;$(AdditionalConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net35'">
<AssemblyTitle>Json.NET .NET 3.5</AssemblyTitle>
@@ -63,7 +63,7 @@
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
<AssemblyTitle>Json.NET .NET Standard 1.3</AssemblyTitle>
<DefineConstants>NETSTANDARD1_3;PORTABLE;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_SERIALIZATION;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_IMMUTABLE_COLLECTIONS;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;HAVE_XML_DOCUMENT;$(AdditionalConstants)</DefineConstants>
<DefineConstants>NETSTANDARD1_3;PORTABLE;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_SERIALIZATION;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_IMMUTABLE_COLLECTIONS;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;HAVE_XML_DOCUMENT;HAVE_CONCURRENT_DICTIONARY;$(AdditionalConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'portable-net40+win8+wpa81+wp8+sl5'">
<AssemblyTitle>Json.NET Portable .NET 4.0</AssemblyTitle>
@@ -24,6 +24,9 @@
#endregion
using System;
#if HAVE_CONCURRENT_DICTIONARY
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json.Utilities;
@@ -69,7 +72,7 @@ namespace Newtonsoft.Json.Serialization
{
private static readonly object TypeContractCacheLock = new object();
private static readonly PropertyNameTable NameTable = new PropertyNameTable();
private static Dictionary<ResolverContractKey, JsonContract> _contractCache;
private static readonly ConcurrentDictionary<ResolverContractKey, JsonContract> _contractCache = new ConcurrentDictionary<ResolverContractKey, JsonContract>();
/// <summary>
/// Initializes a new instance of the <see cref="CamelCasePropertyNamesContractResolver"/> class.
@@ -96,27 +99,8 @@ namespace Newtonsoft.Json.Serialization
}
// for backwards compadibility the CamelCasePropertyNamesContractResolver shares contracts between instances
JsonContract contract;
ResolverContractKey key = new ResolverContractKey(GetType(), type);
Dictionary<ResolverContractKey, JsonContract> cache = _contractCache;
if (cache == null || !cache.TryGetValue(key, out contract))
{
contract = CreateContract(type);
// avoid the possibility of modifying the cache dictionary while another thread is accessing it
lock (TypeContractCacheLock)
{
cache = _contractCache;
Dictionary<ResolverContractKey, JsonContract> updatedCache = (cache != null)
? new Dictionary<ResolverContractKey, JsonContract>(cache)
: new Dictionary<ResolverContractKey, JsonContract>();
updatedCache[key] = contract;
_contractCache = updatedCache;
}
}
return contract;
return _contractCache.GetOrAdd(key, k => CreateContract(type));
}
internal override PropertyNameTable GetNameTable()
@@ -25,6 +25,9 @@
using System;
using System.Collections;
#if HAVE_CONCURRENT_DICTIONARY
using System.Collections.Concurrent;
#endif
using Newtonsoft.Json.Schema;
using System.Collections.Generic;
using System.ComponentModel;
@@ -47,6 +50,7 @@ using Newtonsoft.Json.Utilities.LinqBridge;
using System.Linq;
#endif
using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Serialization
{
@@ -92,7 +96,7 @@ namespace Newtonsoft.Json.Serialization
private readonly object _typeContractCacheLock = new object();
private readonly PropertyNameTable _nameTable = new PropertyNameTable();
private Dictionary<Type, JsonContract> _contractCache;
private readonly ConcurrentDictionary<Type, JsonContract> _contractCache = new ConcurrentDictionary<Type, JsonContract>();
/// <summary>
/// Gets a value indicating whether members are being get and set using dynamic code generation.
@@ -175,26 +179,7 @@ namespace Newtonsoft.Json.Serialization
throw new ArgumentNullException(nameof(type));
}
JsonContract contract;
Dictionary<Type, JsonContract> cache = _contractCache;
if (cache == null || !cache.TryGetValue(type, out contract))
{
contract = CreateContract(type);
// avoid the possibility of modifying the cache dictionary while another thread is accessing it
lock (_typeContractCacheLock)
{
cache = _contractCache;
Dictionary<Type, JsonContract> updatedCache = (cache != null)
? new Dictionary<Type, JsonContract>(cache)
: new Dictionary<Type, JsonContract>();
updatedCache[type] = contract;
_contractCache = updatedCache;
}
}
return contract;
return _contractCache.GetOrAdd(type, CreateContract);
}
/// <summary>
@@ -0,0 +1,76 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !HAVE_CONCURRENT_DICTIONARY
using System;
using System.Collections.Generic;
using System.Threading;
namespace Newtonsoft.Json.Serialization
{
internal sealed class ConcurrentDictionary<TKey, TValue>
{
private readonly object _lock = new object();
private Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
TValue value;
if (!_dictionary.TryGetValue(key, out value))
{
return AddValue(key, valueFactory);
}
return value;
}
private TValue AddValue(TKey key, Func<TKey, TValue> valueFactory)
{
TValue value = valueFactory(key);
lock (_lock)
{
// double check locking
TValue checkValue;
if (_dictionary.TryGetValue(key, out checkValue))
{
return checkValue;
}
Dictionary<TKey, TValue> newDictionary = new Dictionary<TKey, TValue>(_dictionary);
newDictionary[key] = value;
#if HAVE_MEMORY_BARRIER
Thread.MemoryBarrier();
#endif
_dictionary = newDictionary;
}
return value;
}
}
}
#endif
@@ -24,19 +24,16 @@
#endregion
using System;
using System.Collections.Generic;
#if !HAVE_LINQ
using Newtonsoft.Json.Utilities.LinqBridge;
#if HAVE_CONCURRENT_DICTIONARY
using System.Collections.Concurrent;
#endif
using System.Threading;
using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Utilities
{
internal class ThreadSafeStore<TKey, TValue>
{
private readonly object _lock = new object();
private Dictionary<TKey, TValue> _store;
private readonly ConcurrentDictionary<TKey, TValue> _store;
private readonly Func<TKey, TValue> _creator;
public ThreadSafeStore(Func<TKey, TValue> creator)
@@ -47,51 +44,12 @@ namespace Newtonsoft.Json.Utilities
}
_creator = creator;
_store = new Dictionary<TKey, TValue>();
_store = new ConcurrentDictionary<TKey, TValue>();
}
public TValue Get(TKey key)
{
TValue value;
if (!_store.TryGetValue(key, out value))
{
return AddValue(key);
}
return value;
}
private TValue AddValue(TKey key)
{
TValue value = _creator(key);
lock (_lock)
{
if (_store == null)
{
_store = new Dictionary<TKey, TValue>();
_store[key] = value;
}
else
{
// double check locking
TValue checkValue;
if (_store.TryGetValue(key, out checkValue))
{
return checkValue;
}
Dictionary<TKey, TValue> newStore = new Dictionary<TKey, TValue>(_store);
newStore[key] = value;
#if HAVE_MEMORY_BARRIER
Thread.MemoryBarrier();
#endif
_store = newStore;
}
return value;
}
return _store.GetOrAdd(key, _creator);
}
}
}