diff --git a/Src/Newtonsoft.Json.TestConsole/Newtonsoft.Json.TestConsole.csproj b/Src/Newtonsoft.Json.TestConsole/Newtonsoft.Json.TestConsole.csproj index 5cc3904d..4327b18b 100644 --- a/Src/Newtonsoft.Json.TestConsole/Newtonsoft.Json.TestConsole.csproj +++ b/Src/Newtonsoft.Json.TestConsole/Newtonsoft.Json.TestConsole.csproj @@ -1,7 +1,7 @@  Exe - net46 + net46;netcoreapp1.1 Newtonsoft.Json.TestConsole Newtonsoft.Json.TestConsole diff --git a/Src/Newtonsoft.Json.TestConsole/Program.cs b/Src/Newtonsoft.Json.TestConsole/Program.cs index 1bc7d46a..efc6f0ab 100644 --- a/Src/Newtonsoft.Json.TestConsole/Program.cs +++ b/Src/Newtonsoft.Json.TestConsole/Program.cs @@ -26,7 +26,9 @@ using System; using System.Diagnostics; using BenchmarkDotNet.Running; +using Newtonsoft.Json; using Newtonsoft.Json.Tests.Benchmarks; +using System.Reflection; namespace Newtonsoft.Json.TestConsole { @@ -34,8 +36,8 @@ namespace Newtonsoft.Json.TestConsole { public static void Main(string[] args) { - string version = FileVersionInfo.GetVersionInfo(typeof(JsonConvert).Assembly.Location).FileVersion; - Console.WriteLine("Json.NET Version: " + version); + var attribute = (AssemblyFileVersionAttribute)typeof(JsonConvert).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute)); + Console.WriteLine("Json.NET Version: " + attribute.Version); new BenchmarkSwitcher(new [] { typeof(LowLevelBenchmarks) }).Run(new[] { "*" }); } diff --git a/Src/Newtonsoft.Json.Tests/Benchmarks/LowLevelBenchmarks.cs b/Src/Newtonsoft.Json.Tests/Benchmarks/LowLevelBenchmarks.cs index 2adefc6d..4565e430 100644 --- a/Src/Newtonsoft.Json.Tests/Benchmarks/LowLevelBenchmarks.cs +++ b/Src/Newtonsoft.Json.Tests/Benchmarks/LowLevelBenchmarks.cs @@ -62,13 +62,24 @@ namespace Newtonsoft.Json.Tests.Benchmarks [Benchmark] public void DictionaryGet() { - NormalDictionary.TryGetValue("one", out object _); + NormalDictionary.TryGetValue("1", out object _); } [Benchmark] public void ConcurrentDictionaryGet() { - ConcurrentDictionary.TryGetValue("one", out object _); + ConcurrentDictionary.TryGetValue("1", out object _); + } + + [Benchmark] + public void ConcurrentDictionaryGetOrCreate() + { + ConcurrentDictionary.GetOrAdd("1", Dummy); + } + + private object Dummy(string arg) + { + throw new Exception("Should never get here."); } [Benchmark] diff --git a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs index 324d08b1..d093f622 100644 --- a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs @@ -24,9 +24,6 @@ #endregion using System; -#if HAVE_CONCURRENT_DICTIONARY -using System.Collections.Concurrent; -#endif using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json.Utilities; @@ -72,7 +69,7 @@ namespace Newtonsoft.Json.Serialization { private static readonly object TypeContractCacheLock = new object(); private static readonly PropertyNameTable NameTable = new PropertyNameTable(); - private static readonly ConcurrentDictionary _contractCache = new ConcurrentDictionary(); + private static Dictionary _contractCache; /// /// Initializes a new instance of the class. @@ -99,8 +96,27 @@ namespace Newtonsoft.Json.Serialization } // for backwards compadibility the CamelCasePropertyNamesContractResolver shares contracts between instances + JsonContract contract; ResolverContractKey key = new ResolverContractKey(GetType(), type); - return _contractCache.GetOrAdd(key, k => CreateContract(type)); + Dictionary 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 updatedCache = (cache != null) + ? new Dictionary(cache) + : new Dictionary(); + updatedCache[key] = contract; + + _contractCache = updatedCache; + } + } + + return contract; } internal override PropertyNameTable GetNameTable() diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 575ed346..fdd241dd 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -96,7 +96,7 @@ namespace Newtonsoft.Json.Serialization private readonly object _typeContractCacheLock = new object(); private readonly PropertyNameTable _nameTable = new PropertyNameTable(); - private readonly ConcurrentDictionary _contractCache = new ConcurrentDictionary(); + private readonly ThreadSafeStore _contractCache; /// /// Gets a value indicating whether members are being get and set using dynamic code generation. @@ -165,6 +165,8 @@ namespace Newtonsoft.Json.Serialization #pragma warning disable 618 DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public; #pragma warning restore 618 + + _contractCache = new ThreadSafeStore(CreateContract); } /// @@ -179,7 +181,7 @@ namespace Newtonsoft.Json.Serialization throw new ArgumentNullException(nameof(type)); } - return _contractCache.GetOrAdd(type, CreateContract); + return _contractCache.Get(type); } /// diff --git a/Src/Newtonsoft.Json/Utilities/ConcurrentDictionary.cs b/Src/Newtonsoft.Json/Utilities/ConcurrentDictionary.cs deleted file mode 100644 index be1b89a8..00000000 --- a/Src/Newtonsoft.Json/Utilities/ConcurrentDictionary.cs +++ /dev/null @@ -1,76 +0,0 @@ -#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 - { - private readonly object _lock = new object(); - private Dictionary _dictionary = new Dictionary(); - - public TValue GetOrAdd(TKey key, Func valueFactory) - { - TValue value; - if (!_dictionary.TryGetValue(key, out value)) - { - return AddValue(key, valueFactory); - } - - return value; - } - - private TValue AddValue(TKey key, Func valueFactory) - { - TValue value = valueFactory(key); - - lock (_lock) - { - // double check locking - TValue checkValue; - if (_dictionary.TryGetValue(key, out checkValue)) - { - return checkValue; - } - - Dictionary newDictionary = new Dictionary(_dictionary); - newDictionary[key] = value; - -#if HAVE_MEMORY_BARRIER - Thread.MemoryBarrier(); -#endif - _dictionary = newDictionary; - } - - return value; - } - } -} -#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs index 0ab83dcf..2b8f5e49 100644 --- a/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs +++ b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs @@ -24,32 +24,88 @@ #endregion using System; +using System.Collections.Generic; +#if !HAVE_LINQ +using Newtonsoft.Json.Utilities.LinqBridge; +#endif #if HAVE_CONCURRENT_DICTIONARY using System.Collections.Concurrent; #endif +using System.Threading; using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Utilities { internal class ThreadSafeStore { - private readonly ConcurrentDictionary _store; +#if HAVE_CONCURRENT_DICTIONARY + private ConcurrentDictionary _concurrentStore; +#else + private readonly object _lock = new object(); + private Dictionary _store; +#endif private readonly Func _creator; public ThreadSafeStore(Func creator) { - if (creator == null) - { - throw new ArgumentNullException(nameof(creator)); - } + ValidationUtils.ArgumentNotNull(creator, nameof(creator)); _creator = creator; - _store = new ConcurrentDictionary(); +#if HAVE_CONCURRENT_DICTIONARY + _concurrentStore = new ConcurrentDictionary(); +#else + _store = new Dictionary(); +#endif } public TValue Get(TKey key) { - return _store.GetOrAdd(key, _creator); +#if HAVE_CONCURRENT_DICTIONARY + return _concurrentStore.GetOrAdd(key, _creator); +#else + TValue value; + if (!_store.TryGetValue(key, out value)) + { + return AddValue(key); + } + + return value; +#endif } + +#if !HAVE_CONCURRENT_DICTIONARY + private TValue AddValue(TKey key) + { + TValue value = _creator(key); + + lock (_lock) + { + if (_store == null) + { + _store = new Dictionary(); + _store[key] = value; + } + else + { + // double check locking + TValue checkValue; + if (_store.TryGetValue(key, out checkValue)) + { + return checkValue; + } + + Dictionary newStore = new Dictionary(_store); + newStore[key] = value; + +#if HAVE_MEMORY_BARRIER + Thread.MemoryBarrier(); +#endif + _store = newStore; + } + + return value; + } + } +#endif } } \ No newline at end of file