Skip to content

Commit 3906356

Browse files
authored
Merge pull request #498 from ngbrown/NH-3885
NH-3885 - Replace ThreadSafeDictionary wrapper with Framework's ConcurrentDictionary
2 parents 39243fc + 0ece082 commit 3906356

File tree

7 files changed

+79
-265
lines changed

7 files changed

+79
-265
lines changed
Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Threading;
45
using log4net;
5-
using NHibernate.Util;
66
using NUnit.Framework;
77

88
namespace NHibernate.Test.UtilityTest
@@ -23,42 +23,36 @@ public ThreadSafeDictionaryFixture()
2323
[Test, Explicit]
2424
public void MultiThreadAccess()
2525
{
26-
MultiThreadRunner<IDictionary<int, int>>.ExecuteAction[] actions =
27-
new MultiThreadRunner<IDictionary<int, int>>.ExecuteAction[]
26+
MultiThreadRunner<ConcurrentDictionary<int, int>>.ExecuteAction[] actions =
27+
new MultiThreadRunner<ConcurrentDictionary<int, int>>.ExecuteAction[]
2828
{
29-
delegate(IDictionary<int, int> d)
29+
delegate(ConcurrentDictionary<int, int> d)
3030
{
31-
try
32-
{
33-
log.DebugFormat("T{0} Add", Thread.CurrentThread.Name);
34-
write++;
35-
d.Add(rnd.Next(), rnd.Next());
36-
}
37-
catch (ArgumentException)
38-
{
39-
// duplicated key
40-
}
31+
log.DebugFormat("T{0} Add", Thread.CurrentThread.Name);
32+
write++;
33+
d.TryAdd(rnd.Next(), rnd.Next());
4134
},
42-
delegate(IDictionary<int, int> d)
35+
delegate(ConcurrentDictionary<int, int> d)
4336
{
4437
log.DebugFormat("T{0} ContainsKey", Thread.CurrentThread.Name);
4538
read++;
4639
d.ContainsKey(rnd.Next());
4740
},
48-
delegate(IDictionary<int, int> d)
41+
delegate(ConcurrentDictionary<int, int> d)
4942
{
5043
log.DebugFormat("T{0} Remove", Thread.CurrentThread.Name);
5144
write++;
52-
d.Remove(rnd.Next());
45+
int value;
46+
d.TryRemove(rnd.Next(), out value);
5347
},
54-
delegate(IDictionary<int, int> d)
48+
delegate(ConcurrentDictionary<int, int> d)
5549
{
5650
log.DebugFormat("T{0} TryGetValue", Thread.CurrentThread.Name);
5751
read++;
5852
int val;
5953
d.TryGetValue(rnd.Next(), out val);
6054
},
61-
delegate(IDictionary<int, int> d)
55+
delegate(ConcurrentDictionary<int, int> d)
6256
{
6357
try
6458
{
@@ -71,25 +65,25 @@ public void MultiThreadAccess()
7165
// not foud key
7266
}
7367
},
74-
delegate(IDictionary<int, int> d)
68+
delegate(ConcurrentDictionary<int, int> d)
7569
{
7670
log.DebugFormat("T{0} set_this[]", Thread.CurrentThread.Name);
7771
write++;
7872
d[rnd.Next()] = rnd.Next();
7973
},
80-
delegate(IDictionary<int, int> d)
74+
delegate(ConcurrentDictionary<int, int> d)
8175
{
8276
log.DebugFormat("T{0} Keys", Thread.CurrentThread.Name);
8377
read++;
8478
IEnumerable<int> e = d.Keys;
8579
},
86-
delegate(IDictionary<int, int> d)
80+
delegate(ConcurrentDictionary<int, int> d)
8781
{
8882
log.DebugFormat("T{0} Values", Thread.CurrentThread.Name);
8983
read++;
9084
IEnumerable<int> e = d.Values;
9185
},
92-
delegate(IDictionary<int, int> d)
86+
delegate(ConcurrentDictionary<int, int> d)
9387
{
9488
log.DebugFormat("T{0} GetEnumerator", Thread.CurrentThread.Name);
9589
read++;
@@ -99,11 +93,11 @@ public void MultiThreadAccess()
9993
}
10094
},
10195
};
102-
MultiThreadRunner<IDictionary<int, int>> mtr = new MultiThreadRunner<IDictionary<int, int>>(20, actions);
103-
IDictionary<int, int> wrapper = new ThreadSafeDictionary<int, int>(new Dictionary<int, int>());
96+
MultiThreadRunner<ConcurrentDictionary<int, int>> mtr = new MultiThreadRunner<ConcurrentDictionary<int, int>>(20, actions);
97+
ConcurrentDictionary<int, int> wrapper = new ConcurrentDictionary<int, int>();
10498
mtr.EndTimeout = 2000;
10599
mtr.Run(wrapper);
106100
log.DebugFormat("{0} reads, {1} writes -- elements {2}", read, write, wrapper.Count);
107101
}
108102
}
109-
}
103+
}

src/NHibernate/Impl/SessionFactoryImpl.cs

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Data;
45
using System.Linq;
@@ -93,8 +94,8 @@ public void HandleEntityNotFound(string entityName, object id)
9394
private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator();
9495

9596
[NonSerialized]
96-
private readonly ThreadSafeDictionary<string, ICache> allCacheRegions =
97-
new ThreadSafeDictionary<string, ICache>(new Dictionary<string, ICache>());
97+
private readonly ConcurrentDictionary<string, ICache> allCacheRegions =
98+
new ConcurrentDictionary<string, ICache>();
9899

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

149150
[NonSerialized]
150-
private readonly IDictionary<string, IQueryCache> queryCaches;
151+
private readonly ConcurrentDictionary<string, IQueryCache> queryCaches;
151152
[NonSerialized]
152153
private readonly SchemaExport schemaExport;
153154
[NonSerialized]
@@ -160,7 +161,7 @@ public void HandleEntityNotFound(string entityName, object id)
160161
[NonSerialized]
161162
private readonly UpdateTimestampsCache updateTimestampsCache;
162163
[NonSerialized]
163-
private readonly IDictionary<string, string[]> entityNameImplementorsMap = new ThreadSafeDictionary<string, string[]>(new Dictionary<string, string[]>(100));
164+
private readonly IDictionary<string, string[]> entityNameImplementorsMap = new ConcurrentDictionary<string, string[]>(4 * System.Environment.ProcessorCount, 100);
164165
private readonly string uuid;
165166
private bool disposed;
166167

@@ -247,7 +248,10 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
247248
if (cache != null)
248249
{
249250
caches.Add(cacheRegion, cache);
250-
allCacheRegions.Add(cache.RegionName, cache.Cache);
251+
if (!allCacheRegions.TryAdd(cache.RegionName, cache.Cache))
252+
{
253+
throw new HibernateException("An item with the same key has already been added to allCacheRegions.");
254+
}
251255
}
252256
}
253257
IEntityPersister cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping);
@@ -375,7 +379,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
375379
{
376380
updateTimestampsCache = new UpdateTimestampsCache(settings, properties);
377381
queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings, properties);
378-
queryCaches = new ThreadSafeDictionary<string, IQueryCache>(new Dictionary<string, IQueryCache>());
382+
queryCaches = new ConcurrentDictionary<string, IQueryCache>();
379383
}
380384
else
381385
{
@@ -967,10 +971,8 @@ public UpdateTimestampsCache UpdateTimestampsCache
967971

968972
public IDictionary<string, ICache> GetAllSecondLevelCacheRegions()
969973
{
970-
lock (allCacheRegions.SyncRoot)
971-
{
972-
return new Dictionary<string, ICache>(allCacheRegions);
973-
}
974+
// ToArray creates a moment in time snapshot
975+
return allCacheRegions.ToArray().ToDictionary(kv => kv.Key, kv => kv.Value);
974976
}
975977

976978
public ICache GetSecondLevelCacheRegion(string regionName)
@@ -1002,18 +1004,14 @@ public IQueryCache GetQueryCache(string cacheRegion)
10021004
return null;
10031005
}
10041006

1005-
lock (allCacheRegions.SyncRoot)
1006-
{
1007-
IQueryCache currentQueryCache;
1008-
if (!queryCaches.TryGetValue(cacheRegion, out currentQueryCache))
1007+
return queryCaches.GetOrAdd(
1008+
cacheRegion,
1009+
cr =>
10091010
{
1010-
currentQueryCache =
1011-
settings.QueryCacheFactory.GetQueryCache(cacheRegion, updateTimestampsCache, settings, properties);
1012-
queryCaches[cacheRegion] = currentQueryCache;
1011+
IQueryCache currentQueryCache = settings.QueryCacheFactory.GetQueryCache(cacheRegion, updateTimestampsCache, settings, properties);
10131012
allCacheRegions[currentQueryCache.RegionName] = currentQueryCache.Cache;
1014-
}
1015-
return currentQueryCache;
1016-
}
1013+
return currentQueryCache;
1014+
});
10171015
}
10181016

10191017
public void EvictQueries()
@@ -1277,4 +1275,4 @@ public string Uuid
12771275

12781276
#endregion
12791277
}
1280-
}
1278+
}

src/NHibernate/NHibernate.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1783,7 +1783,6 @@
17831783
<Compile Include="Util\SimpleMRUCache.cs" />
17841784
<Compile Include="Util\SingletonEnumerable.cs" />
17851785
<Compile Include="Util\SoftLimitMRUCache.cs" />
1786-
<Compile Include="Util\ThreadSafeDictionary.cs" />
17871786
<Compile Include="Util\TypeNameParser.cs" />
17881787
<Compile Include="Util\UnmodifiableDictionary.cs" />
17891788
<Compile Include="Util\WeakHashtable.cs" />

src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66

77
#endregion
88

9-
using System.Collections.Generic;
10-
using NHibernate.Util;
9+
using System.Collections.Concurrent;
1110

1211
namespace NHibernate.Proxy.DynamicProxy
1312
{
1413
public class ProxyCache : IProxyCache
1514
{
16-
private static readonly IDictionary<ProxyCacheEntry, System.Type> cache = new ThreadSafeDictionary<ProxyCacheEntry, System.Type>(new Dictionary<ProxyCacheEntry, System.Type>());
15+
private static readonly ConcurrentDictionary<ProxyCacheEntry, System.Type> cache = new ConcurrentDictionary<ProxyCacheEntry, System.Type>();
1716

1817
#region IProxyCache Members
1918

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

5453
#endregion
5554
}
56-
}
55+
}

src/NHibernate/SqlTypes/SqlTypeFactory.cs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Data;
3-
using System.Collections.Generic;
44
using System.Runtime.CompilerServices;
5-
using NHibernate.Util;
65

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

2019
public static readonly SqlType Guid = new SqlType(DbType.Guid);
2120
public static readonly SqlType Boolean = new SqlType(DbType.Boolean);
@@ -44,30 +43,14 @@ public static class SqlTypeFactory
4443
private static T GetTypeWithLen<T>(int length, TypeWithLenCreateDelegate createDelegate) where T : SqlType
4544
{
4645
string key = GetKeyForLengthBased(typeof (T).Name, length);
47-
SqlType result;
48-
if (!SqlTypes.TryGetValue(key, out result))
49-
{
50-
lock(SqlTypes)
51-
{
52-
if (!SqlTypes.TryGetValue(key, out result))
53-
{
54-
result = createDelegate(length);
55-
SqlTypes.Add(key, result);
56-
}
57-
}
58-
}
46+
SqlType result = SqlTypes.GetOrAdd(key, k => createDelegate(length));
5947
return (T) result;
6048
}
6149

6250
private static SqlType GetTypeWithPrecision(DbType dbType, byte precision, byte scale)
6351
{
6452
string key = GetKeyForPrecisionScaleBased(dbType.ToString(), precision, scale);
65-
SqlType result;
66-
if (!SqlTypes.TryGetValue(key, out result))
67-
{
68-
result = new SqlType(dbType, precision, scale);
69-
SqlTypes.Add(key, result);
70-
}
53+
SqlType result = SqlTypes.GetOrAdd(key, k => new SqlType(dbType, precision, scale));
7154
return result;
7255
}
7356

@@ -112,4 +95,4 @@ private static string GetKeyForPrecisionScaleBased(string name, byte precision,
11295
return name + "(" + precision + ", " + scale + ")";
11396
}
11497
}
115-
}
98+
}

src/NHibernate/Type/TypeFactory.cs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using System;
2-
using System.Collections;
2+
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.Globalization;
55
using System.Reflection;
66
using System.Xml;
77
using System.Xml.Linq;
88
using NHibernate.Bytecode;
99
using NHibernate.Classic;
10-
using NHibernate.Engine;
1110
using NHibernate.SqlTypes;
1211
using NHibernate.UserTypes;
1312
using NHibernate.Util;
@@ -60,14 +59,14 @@ private enum TypeClassification
6059
* "System.String(l)" -> instance of StringType with specified l
6160
*/
6261

63-
private static readonly IDictionary<string, IType> typeByTypeOfName =
64-
new ThreadSafeDictionary<string, IType>(new Dictionary<string, IType>());
62+
private static readonly ConcurrentDictionary<string, IType> typeByTypeOfName =
63+
new ConcurrentDictionary<string, IType>();
6564

66-
private static readonly IDictionary<string, GetNullableTypeWithLength> getTypeDelegatesWithLength =
67-
new ThreadSafeDictionary<string, GetNullableTypeWithLength>(new Dictionary<string, GetNullableTypeWithLength>());
65+
private static readonly ConcurrentDictionary<string, GetNullableTypeWithLength> getTypeDelegatesWithLength =
66+
new ConcurrentDictionary<string, GetNullableTypeWithLength>();
6867

69-
private static readonly IDictionary<string, GetNullableTypeWithPrecision> getTypeDelegatesWithPrecision =
70-
new ThreadSafeDictionary<string, GetNullableTypeWithPrecision>(new Dictionary<string, GetNullableTypeWithPrecision>());
68+
private static readonly ConcurrentDictionary<string, GetNullableTypeWithPrecision> getTypeDelegatesWithPrecision =
69+
new ConcurrentDictionary<string, GetNullableTypeWithPrecision>();
7170

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

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

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

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

403408
private static void AddToTypeOfName(string key, IType type)
404409
{
405-
typeByTypeOfName.Add(key, type);
406-
typeByTypeOfName.Add(type.Name, type);
410+
if (!typeByTypeOfName.TryAdd(key, type))
411+
{
412+
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
413+
}
414+
if (!typeByTypeOfName.TryAdd(type.Name, type))
415+
{
416+
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
417+
}
407418
}
408419

409420
private static void AddToTypeOfNameWithLength(string key, IType type)
410421
{
411-
typeByTypeOfName.Add(key, type);
422+
if (!typeByTypeOfName.TryAdd(key, type))
423+
{
424+
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
425+
}
412426
}
413427

414428
private static void AddToTypeOfNameWithPrecision(string key, IType type)
415429
{
416-
typeByTypeOfName.Add(key, type);
430+
if (!typeByTypeOfName.TryAdd(key, type))
431+
{
432+
throw new HibernateException("An item with the same key has already been added to typeByTypeOfName.");
433+
}
417434
}
418435

419436
private static string GetKeyForLengthBased(string name, int length)

0 commit comments

Comments
 (0)