Skip to content

NH-3885 - Replace ThreadSafeDictionary wrapper with Framework's ConcurrentDictionary #498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 20 additions & 26 deletions src/NHibernate.Test/UtilityTest/ThreadSafeDictionaryFixture.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using log4net;
using NHibernate.Util;
using NUnit.Framework;

namespace NHibernate.Test.UtilityTest
Expand All @@ -23,42 +23,36 @@ public ThreadSafeDictionaryFixture()
[Test, Explicit]
public void MultiThreadAccess()
{
MultiThreadRunner<IDictionary<int, int>>.ExecuteAction[] actions =
new MultiThreadRunner<IDictionary<int, int>>.ExecuteAction[]
MultiThreadRunner<ConcurrentDictionary<int, int>>.ExecuteAction[] actions =
new MultiThreadRunner<ConcurrentDictionary<int, int>>.ExecuteAction[]
{
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
try
{
log.DebugFormat("T{0} Add", Thread.CurrentThread.Name);
write++;
d.Add(rnd.Next(), rnd.Next());
}
catch (ArgumentException)
{
// duplicated key
}
log.DebugFormat("T{0} Add", Thread.CurrentThread.Name);
write++;
d.TryAdd(rnd.Next(), rnd.Next());
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} ContainsKey", Thread.CurrentThread.Name);
read++;
d.ContainsKey(rnd.Next());
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} Remove", Thread.CurrentThread.Name);
write++;
d.Remove(rnd.Next());
int value;
d.TryRemove(rnd.Next(), out value);
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} TryGetValue", Thread.CurrentThread.Name);
read++;
int val;
d.TryGetValue(rnd.Next(), out val);
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
try
{
Expand All @@ -71,25 +65,25 @@ public void MultiThreadAccess()
// not foud key
}
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} set_this[]", Thread.CurrentThread.Name);
write++;
d[rnd.Next()] = rnd.Next();
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} Keys", Thread.CurrentThread.Name);
read++;
IEnumerable<int> e = d.Keys;
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} Values", Thread.CurrentThread.Name);
read++;
IEnumerable<int> e = d.Values;
},
delegate(IDictionary<int, int> d)
delegate(ConcurrentDictionary<int, int> d)
{
log.DebugFormat("T{0} GetEnumerator", Thread.CurrentThread.Name);
read++;
Expand All @@ -99,11 +93,11 @@ public void MultiThreadAccess()
}
},
};
MultiThreadRunner<IDictionary<int, int>> mtr = new MultiThreadRunner<IDictionary<int, int>>(20, actions);
IDictionary<int, int> wrapper = new ThreadSafeDictionary<int, int>(new Dictionary<int, int>());
MultiThreadRunner<ConcurrentDictionary<int, int>> mtr = new MultiThreadRunner<ConcurrentDictionary<int, int>>(20, actions);
ConcurrentDictionary<int, int> wrapper = new ConcurrentDictionary<int, int>();
mtr.EndTimeout = 2000;
mtr.Run(wrapper);
log.DebugFormat("{0} reads, {1} writes -- elements {2}", read, write, wrapper.Count);
}
}
}
}
40 changes: 19 additions & 21 deletions src/NHibernate/Impl/SessionFactoryImpl.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
Expand Down Expand Up @@ -93,8 +94,8 @@ public void HandleEntityNotFound(string entityName, object id)
private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator();

[NonSerialized]
private readonly ThreadSafeDictionary<string, ICache> allCacheRegions =
new ThreadSafeDictionary<string, ICache>(new Dictionary<string, ICache>());
private readonly ConcurrentDictionary<string, ICache> allCacheRegions =
new ConcurrentDictionary<string, ICache>();

[NonSerialized]
private readonly IDictionary<string, IClassMetadata> classMetadata;
Expand Down Expand Up @@ -147,7 +148,7 @@ public void HandleEntityNotFound(string entityName, object id)
private readonly IQueryCache queryCache;

[NonSerialized]
private readonly IDictionary<string, IQueryCache> queryCaches;
private readonly ConcurrentDictionary<string, IQueryCache> queryCaches;
[NonSerialized]
private readonly SchemaExport schemaExport;
[NonSerialized]
Expand All @@ -160,7 +161,7 @@ public void HandleEntityNotFound(string entityName, object id)
[NonSerialized]
private readonly UpdateTimestampsCache updateTimestampsCache;
[NonSerialized]
private readonly IDictionary<string, string[]> entityNameImplementorsMap = new ThreadSafeDictionary<string, string[]>(new Dictionary<string, string[]>(100));
private readonly IDictionary<string, string[]> entityNameImplementorsMap = new ConcurrentDictionary<string, string[]>(4 * System.Environment.ProcessorCount, 100);
private readonly string uuid;
private bool disposed;

Expand Down Expand Up @@ -247,7 +248,10 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
if (cache != null)
{
caches.Add(cacheRegion, cache);
allCacheRegions.Add(cache.RegionName, cache.Cache);
if (!allCacheRegions.TryAdd(cache.RegionName, cache.Cache))
{
throw new HibernateException("An item with the same key has already been added to allCacheRegions.");
}
}
}
IEntityPersister cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping);
Expand Down Expand Up @@ -375,7 +379,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
{
updateTimestampsCache = new UpdateTimestampsCache(settings, properties);
queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings, properties);
queryCaches = new ThreadSafeDictionary<string, IQueryCache>(new Dictionary<string, IQueryCache>());
queryCaches = new ConcurrentDictionary<string, IQueryCache>();
}
else
{
Expand Down Expand Up @@ -967,10 +971,8 @@ public UpdateTimestampsCache UpdateTimestampsCache

public IDictionary<string, ICache> GetAllSecondLevelCacheRegions()
{
lock (allCacheRegions.SyncRoot)
{
return new Dictionary<string, ICache>(allCacheRegions);
}
// ToArray creates a moment in time snapshot
return allCacheRegions.ToArray().ToDictionary(kv => kv.Key, kv => kv.Value);
}

public ICache GetSecondLevelCacheRegion(string regionName)
Expand Down Expand Up @@ -1002,18 +1004,14 @@ public IQueryCache GetQueryCache(string cacheRegion)
return null;
}

lock (allCacheRegions.SyncRoot)
{
IQueryCache currentQueryCache;
if (!queryCaches.TryGetValue(cacheRegion, out currentQueryCache))
return queryCaches.GetOrAdd(
cacheRegion,
cr =>
{
currentQueryCache =
settings.QueryCacheFactory.GetQueryCache(cacheRegion, updateTimestampsCache, settings, properties);
queryCaches[cacheRegion] = currentQueryCache;
IQueryCache currentQueryCache = settings.QueryCacheFactory.GetQueryCache(cacheRegion, updateTimestampsCache, settings, properties);
allCacheRegions[currentQueryCache.RegionName] = currentQueryCache.Cache;
}
return currentQueryCache;
}
return currentQueryCache;
});
}

public void EvictQueries()
Expand Down Expand Up @@ -1277,4 +1275,4 @@ public string Uuid

#endregion
}
}
}
1 change: 0 additions & 1 deletion src/NHibernate/NHibernate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,6 @@
<Compile Include="Util\SimpleMRUCache.cs" />
<Compile Include="Util\SingletonEnumerable.cs" />
<Compile Include="Util\SoftLimitMRUCache.cs" />
<Compile Include="Util\ThreadSafeDictionary.cs" />
<Compile Include="Util\TypeNameParser.cs" />
<Compile Include="Util\UnmodifiableDictionary.cs" />
<Compile Include="Util\WeakHashtable.cs" />
Expand Down
7 changes: 3 additions & 4 deletions src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

#endregion

using System.Collections.Generic;
using NHibernate.Util;
using System.Collections.Concurrent;

namespace NHibernate.Proxy.DynamicProxy
{
public class ProxyCache : IProxyCache
{
private static readonly IDictionary<ProxyCacheEntry, System.Type> cache = new ThreadSafeDictionary<ProxyCacheEntry, System.Type>(new Dictionary<ProxyCacheEntry, System.Type>());
private static readonly ConcurrentDictionary<ProxyCacheEntry, System.Type> cache = new ConcurrentDictionary<ProxyCacheEntry, System.Type>();

#region IProxyCache Members

Expand Down Expand Up @@ -53,4 +52,4 @@ public void StoreProxyType(System.Type result, System.Type baseType, params Syst

#endregion
}
}
}
29 changes: 6 additions & 23 deletions src/NHibernate/SqlTypes/SqlTypeFactory.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Data;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using NHibernate.Util;

namespace NHibernate.SqlTypes
{
Expand All @@ -14,8 +13,8 @@ public static class SqlTypeFactory
{
// key = typeof(sqlType).Name : ie - BinarySqlType(l), BooleanSqlType, DecimalSqlType(p,s)
// value = SqlType
private static readonly IDictionary<string, SqlType> SqlTypes =
new ThreadSafeDictionary<string, SqlType>(new Dictionary<string, SqlType>(128));
private static readonly ConcurrentDictionary<string, SqlType> SqlTypes =
new ConcurrentDictionary<string, SqlType>(4 * System.Environment.ProcessorCount, 128);

public static readonly SqlType Guid = new SqlType(DbType.Guid);
public static readonly SqlType Boolean = new SqlType(DbType.Boolean);
Expand Down Expand Up @@ -44,30 +43,14 @@ public static class SqlTypeFactory
private static T GetTypeWithLen<T>(int length, TypeWithLenCreateDelegate createDelegate) where T : SqlType
{
string key = GetKeyForLengthBased(typeof (T).Name, length);
SqlType result;
if (!SqlTypes.TryGetValue(key, out result))
{
lock(SqlTypes)
{
if (!SqlTypes.TryGetValue(key, out result))
{
result = createDelegate(length);
SqlTypes.Add(key, result);
}
}
}
SqlType result = SqlTypes.GetOrAdd(key, k => createDelegate(length));
return (T) result;
}

private static SqlType GetTypeWithPrecision(DbType dbType, byte precision, byte scale)
{
string key = GetKeyForPrecisionScaleBased(dbType.ToString(), precision, scale);
SqlType result;
if (!SqlTypes.TryGetValue(key, out result))
{
result = new SqlType(dbType, precision, scale);
SqlTypes.Add(key, result);
}
SqlType result = SqlTypes.GetOrAdd(key, k => new SqlType(dbType, precision, scale));
return result;
}

Expand Down Expand Up @@ -112,4 +95,4 @@ private static string GetKeyForPrecisionScaleBased(string name, byte precision,
return name + "(" + precision + ", " + scale + ")";
}
}
}
}
45 changes: 31 additions & 14 deletions src/NHibernate/Type/TypeFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using NHibernate.Bytecode;
using NHibernate.Classic;
using NHibernate.Engine;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
using NHibernate.Util;
Expand Down Expand Up @@ -60,14 +59,14 @@ private enum TypeClassification
* "System.String(l)" -> instance of StringType with specified l
*/

private static readonly IDictionary<string, IType> typeByTypeOfName =
new ThreadSafeDictionary<string, IType>(new Dictionary<string, IType>());
private static readonly ConcurrentDictionary<string, IType> typeByTypeOfName =
new ConcurrentDictionary<string, IType>();

private static readonly IDictionary<string, GetNullableTypeWithLength> getTypeDelegatesWithLength =
new ThreadSafeDictionary<string, GetNullableTypeWithLength>(new Dictionary<string, GetNullableTypeWithLength>());
private static readonly ConcurrentDictionary<string, GetNullableTypeWithLength> getTypeDelegatesWithLength =
new ConcurrentDictionary<string, GetNullableTypeWithLength>();

private static readonly IDictionary<string, GetNullableTypeWithPrecision> getTypeDelegatesWithPrecision =
new ThreadSafeDictionary<string, GetNullableTypeWithPrecision>(new Dictionary<string, GetNullableTypeWithPrecision>());
private static readonly ConcurrentDictionary<string, GetNullableTypeWithPrecision> getTypeDelegatesWithPrecision =
new ConcurrentDictionary<string, GetNullableTypeWithPrecision>();

private delegate NullableType GetNullableTypeWithLength(int length); // Func<int, NullableType>

Expand Down Expand Up @@ -133,7 +132,10 @@ private static void RegisterType(IType nhibernateType, IEnumerable<string> alias
foreach (var alias in typeAliases)
{
typeByTypeOfName[alias] = nhibernateType;
getTypeDelegatesWithLength.Add(alias, ctorLength);
if (!getTypeDelegatesWithLength.TryAdd(alias, ctorLength))
{
throw new HibernateException("An item with the same key has already been added to getTypeDelegatesWithLength.");
}
}
}

Expand All @@ -143,7 +145,10 @@ private static void RegisterType(IType nhibernateType, IEnumerable<string> alias
foreach (var alias in typeAliases)
{
typeByTypeOfName[alias] = nhibernateType;
getTypeDelegatesWithPrecision.Add(alias, ctorPrecision);
if (!getTypeDelegatesWithPrecision.TryAdd(alias, ctorPrecision))
{
throw new HibernateException("An item with the same key has already been added to getTypeDelegatesWithPrecision.");
}
}
}

Expand Down Expand Up @@ -402,18 +407,30 @@ internal static IType BuiltInType(string typeName, byte precision, byte scale)

private static void AddToTypeOfName(string key, IType type)
{
typeByTypeOfName.Add(key, type);
typeByTypeOfName.Add(type.Name, type);
if (!typeByTypeOfName.TryAdd(key, type))
{
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
}
if (!typeByTypeOfName.TryAdd(type.Name, type))
{
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
}
}

private static void AddToTypeOfNameWithLength(string key, IType type)
{
typeByTypeOfName.Add(key, type);
if (!typeByTypeOfName.TryAdd(key, type))
{
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
}
}

private static void AddToTypeOfNameWithPrecision(string key, IType type)
{
typeByTypeOfName.Add(key, type);
if (!typeByTypeOfName.TryAdd(key, type))
{
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
}
}

private static string GetKeyForLengthBased(string name, int length)
Expand Down
Loading