Skip to content

Commit 2e25b48

Browse files
ngbrownoskarb
authored andcommitted
NH-3885 - Replace ThreadSafeDictionary wrapper with Framework's ConcurrentDictionary.
Backport of 925a9f0 and e6aa1c4 to 4.1.x. Only difference is that on the older branch, we leave the ThreadSafeDictionary class to avoid changing public API. /OB 2016-12-04.
1 parent 7678f1f commit 2e25b48

File tree

5 files changed

+79
-88
lines changed

5 files changed

+79
-88
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/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)