diff --git a/src/NHibernate.Test/UtilityTest/ThreadSafeDictionaryFixture.cs b/src/NHibernate.Test/UtilityTest/ThreadSafeDictionaryFixture.cs index b8ce2f1c611..d5444f7a756 100644 --- a/src/NHibernate.Test/UtilityTest/ThreadSafeDictionaryFixture.cs +++ b/src/NHibernate.Test/UtilityTest/ThreadSafeDictionaryFixture.cs @@ -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 @@ -23,42 +23,36 @@ public ThreadSafeDictionaryFixture() [Test, Explicit] public void MultiThreadAccess() { - MultiThreadRunner>.ExecuteAction[] actions = - new MultiThreadRunner>.ExecuteAction[] + MultiThreadRunner>.ExecuteAction[] actions = + new MultiThreadRunner>.ExecuteAction[] { - delegate(IDictionary d) + delegate(ConcurrentDictionary 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 d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} ContainsKey", Thread.CurrentThread.Name); read++; d.ContainsKey(rnd.Next()); }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} Remove", Thread.CurrentThread.Name); write++; - d.Remove(rnd.Next()); + int value; + d.TryRemove(rnd.Next(), out value); }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} TryGetValue", Thread.CurrentThread.Name); read++; int val; d.TryGetValue(rnd.Next(), out val); }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { try { @@ -71,25 +65,25 @@ public void MultiThreadAccess() // not foud key } }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} set_this[]", Thread.CurrentThread.Name); write++; d[rnd.Next()] = rnd.Next(); }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} Keys", Thread.CurrentThread.Name); read++; IEnumerable e = d.Keys; }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} Values", Thread.CurrentThread.Name); read++; IEnumerable e = d.Values; }, - delegate(IDictionary d) + delegate(ConcurrentDictionary d) { log.DebugFormat("T{0} GetEnumerator", Thread.CurrentThread.Name); read++; @@ -99,11 +93,11 @@ public void MultiThreadAccess() } }, }; - MultiThreadRunner> mtr = new MultiThreadRunner>(20, actions); - IDictionary wrapper = new ThreadSafeDictionary(new Dictionary()); + MultiThreadRunner> mtr = new MultiThreadRunner>(20, actions); + ConcurrentDictionary wrapper = new ConcurrentDictionary(); mtr.EndTimeout = 2000; mtr.Run(wrapper); log.DebugFormat("{0} reads, {1} writes -- elements {2}", read, write, wrapper.Count); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index ac08d74c2e5..e5067d8d5b0 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; @@ -93,8 +94,8 @@ public void HandleEntityNotFound(string entityName, object id) private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator(); [NonSerialized] - private readonly ThreadSafeDictionary allCacheRegions = - new ThreadSafeDictionary(new Dictionary()); + private readonly ConcurrentDictionary allCacheRegions = + new ConcurrentDictionary(); [NonSerialized] private readonly IDictionary classMetadata; @@ -147,7 +148,7 @@ public void HandleEntityNotFound(string entityName, object id) private readonly IQueryCache queryCache; [NonSerialized] - private readonly IDictionary queryCaches; + private readonly ConcurrentDictionary queryCaches; [NonSerialized] private readonly SchemaExport schemaExport; [NonSerialized] @@ -160,7 +161,7 @@ public void HandleEntityNotFound(string entityName, object id) [NonSerialized] private readonly UpdateTimestampsCache updateTimestampsCache; [NonSerialized] - private readonly IDictionary entityNameImplementorsMap = new ThreadSafeDictionary(new Dictionary(100)); + private readonly IDictionary entityNameImplementorsMap = new ConcurrentDictionary(4 * System.Environment.ProcessorCount, 100); private readonly string uuid; private bool disposed; @@ -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); @@ -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(new Dictionary()); + queryCaches = new ConcurrentDictionary(); } else { @@ -967,10 +971,8 @@ public UpdateTimestampsCache UpdateTimestampsCache public IDictionary GetAllSecondLevelCacheRegions() { - lock (allCacheRegions.SyncRoot) - { - return new Dictionary(allCacheRegions); - } + // ToArray creates a moment in time snapshot + return allCacheRegions.ToArray().ToDictionary(kv => kv.Key, kv => kv.Value); } public ICache GetSecondLevelCacheRegion(string regionName) @@ -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() @@ -1277,4 +1275,4 @@ public string Uuid #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index 4c07150826f..b4c67ec7813 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -1783,7 +1783,6 @@ - diff --git a/src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs b/src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs index b9741a9c0a0..340e3308706 100644 --- a/src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs +++ b/src/NHibernate/Proxy/DynamicProxy/ProxyCache.cs @@ -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 cache = new ThreadSafeDictionary(new Dictionary()); + private static readonly ConcurrentDictionary cache = new ConcurrentDictionary(); #region IProxyCache Members @@ -53,4 +52,4 @@ public void StoreProxyType(System.Type result, System.Type baseType, params Syst #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/SqlTypes/SqlTypeFactory.cs b/src/NHibernate/SqlTypes/SqlTypeFactory.cs index aec3f49fb1d..290d4472f5a 100644 --- a/src/NHibernate/SqlTypes/SqlTypeFactory.cs +++ b/src/NHibernate/SqlTypes/SqlTypeFactory.cs @@ -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 { @@ -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 SqlTypes = - new ThreadSafeDictionary(new Dictionary(128)); + private static readonly ConcurrentDictionary SqlTypes = + new ConcurrentDictionary(4 * System.Environment.ProcessorCount, 128); public static readonly SqlType Guid = new SqlType(DbType.Guid); public static readonly SqlType Boolean = new SqlType(DbType.Boolean); @@ -44,30 +43,14 @@ public static class SqlTypeFactory private static T GetTypeWithLen(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; } @@ -112,4 +95,4 @@ private static string GetKeyForPrecisionScaleBased(string name, byte precision, return name + "(" + precision + ", " + scale + ")"; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Type/TypeFactory.cs b/src/NHibernate/Type/TypeFactory.cs index 84214dc31a6..4a95b4dba19 100644 --- a/src/NHibernate/Type/TypeFactory.cs +++ b/src/NHibernate/Type/TypeFactory.cs @@ -1,5 +1,5 @@ using System; -using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Reflection; @@ -7,7 +7,6 @@ using System.Xml.Linq; using NHibernate.Bytecode; using NHibernate.Classic; -using NHibernate.Engine; using NHibernate.SqlTypes; using NHibernate.UserTypes; using NHibernate.Util; @@ -60,14 +59,14 @@ private enum TypeClassification * "System.String(l)" -> instance of StringType with specified l */ - private static readonly IDictionary typeByTypeOfName = - new ThreadSafeDictionary(new Dictionary()); + private static readonly ConcurrentDictionary typeByTypeOfName = + new ConcurrentDictionary(); - private static readonly IDictionary getTypeDelegatesWithLength = - new ThreadSafeDictionary(new Dictionary()); + private static readonly ConcurrentDictionary getTypeDelegatesWithLength = + new ConcurrentDictionary(); - private static readonly IDictionary getTypeDelegatesWithPrecision = - new ThreadSafeDictionary(new Dictionary()); + private static readonly ConcurrentDictionary getTypeDelegatesWithPrecision = + new ConcurrentDictionary(); private delegate NullableType GetNullableTypeWithLength(int length); // Func @@ -133,7 +132,10 @@ private static void RegisterType(IType nhibernateType, IEnumerable 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."); + } } } @@ -143,7 +145,10 @@ private static void RegisterType(IType nhibernateType, IEnumerable 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."); + } } } @@ -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) diff --git a/src/NHibernate/Util/ThreadSafeDictionary.cs b/src/NHibernate/Util/ThreadSafeDictionary.cs deleted file mode 100644 index 302e95e2459..00000000000 --- a/src/NHibernate/Util/ThreadSafeDictionary.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; - -namespace NHibernate.Util -{ - [Serializable] - public class ThreadSafeDictionary : IDictionary - { - private object _syncRoot; - - private readonly IDictionary dictionary; - - public ThreadSafeDictionary(IDictionary dictionary) - { - this.dictionary = dictionary; - } - - public object SyncRoot - { - get - { - if (_syncRoot == null) - { - Interlocked.CompareExchange(ref _syncRoot, new object(), null); - } - - return _syncRoot; - } - } - - #region IDictionary Members - - public bool ContainsKey(TKey key) - { - return dictionary.ContainsKey(key); - } - - public void Add(TKey key, TValue value) - { - lock (SyncRoot) - { - dictionary.Add(key, value); - } - } - - public bool Remove(TKey key) - { - lock (SyncRoot) - { - return dictionary.Remove(key); - } - } - - public bool TryGetValue(TKey key, out TValue value) - { - return dictionary.TryGetValue(key, out value); - } - - public TValue this[TKey key] - { - get { return dictionary[key]; } - set - { - lock (SyncRoot) - { - dictionary[key] = value; - } - } - } - - public ICollection Keys - { - get - { - lock (SyncRoot) - { - return dictionary.Keys; - } - } - } - - public ICollection Values - { - get - { - lock (SyncRoot) - { - return dictionary.Values; - } - } - } - - #endregion - - #region ICollection> Members - - public void Add(KeyValuePair item) - { - lock (SyncRoot) - { - dictionary.Add(item); - } - } - - public void Clear() - { - lock (SyncRoot) - { - dictionary.Clear(); - } - } - - public bool Contains(KeyValuePair item) - { - return dictionary.Contains(item); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - lock (SyncRoot) - { - dictionary.CopyTo(array, arrayIndex); - } - } - - public bool Remove(KeyValuePair item) - { - lock (SyncRoot) - { - return dictionary.Remove(item); - } - } - - public int Count - { - get { return dictionary.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - #endregion - - #region IEnumerable> Members - - IEnumerator> IEnumerable>.GetEnumerator() - { - // In a multi thread environment the dictionary can be changed by another - // thread than the actual that asking for a enumerator. - // The Dictionary create a new KeyValuePair for each iteration so.... - // we can create a snapshot for the thread preventing InvalidOperationException when - // another thread change the version of the dictionary. - lock (SyncRoot) - { - KeyValuePair[] pairArray = new KeyValuePair[dictionary.Count]; - dictionary.CopyTo(pairArray, 0); - return Array.AsReadOnly(pairArray).GetEnumerator(); - } - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - return ((IEnumerable>) this).GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file