This is an automated email from the ASF dual-hosted git repository. nightowl888 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/lucenenet.git
commit b4e6f0fa994797f9e743ec23b191d43a06942b86 Author: Shad Storhaug <[email protected]> AuthorDate: Mon Jan 27 03:35:31 2020 +0700 Lucene.Net.Search.FieldCacheImpl: Switched to ConcurrentDictionary for the innerCache to try to reduce locking (LUCENENET-610) --- src/Lucene.Net/Search/FieldCacheImpl.cs | 112 ++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/Lucene.Net/Search/FieldCacheImpl.cs b/src/Lucene.Net/Search/FieldCacheImpl.cs index eaa158e..87e6a95 100644 --- a/src/Lucene.Net/Search/FieldCacheImpl.cs +++ b/src/Lucene.Net/Search/FieldCacheImpl.cs @@ -2,11 +2,13 @@ using Lucene.Net.Index; using Lucene.Net.Support; using Lucene.Net.Support.IO; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading; namespace Lucene.Net.Search { @@ -114,9 +116,11 @@ namespace Lucene.Net.Search { Cache cache = cacheEntry.Value; Type cacheType = cacheEntry.Key; +#if !FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR lock (cache.readerCache) { - foreach (KeyValuePair<object, IDictionary<CacheKey, object>> readerCacheEntry in cache.readerCache) +#endif + foreach (var readerCacheEntry in cache.readerCache) { object readerKey = readerCacheEntry.Key; if (readerKey == null) @@ -130,7 +134,9 @@ namespace Lucene.Net.Search result.Add(new FieldCache.CacheEntry(readerKey, entry.field, cacheType, entry.custom, mapEntry.Value)); } } +#if !FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR } +#endif } return result.ToArray(); } @@ -208,9 +214,9 @@ namespace Lucene.Net.Search internal readonly FieldCacheImpl wrapper; #if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR - internal ConditionalWeakTable<object, IDictionary<CacheKey, object>> readerCache = new ConditionalWeakTable<object, IDictionary<CacheKey, object>>(); + internal ConditionalWeakTable<object, ConcurrentDictionary<CacheKey, object>> readerCache = new ConditionalWeakTable<object, ConcurrentDictionary<CacheKey, object>>(); #else - internal WeakDictionary<object, IDictionary<CacheKey, object>> readerCache = new WeakDictionary<object, IDictionary<CacheKey, object>>(); + internal WeakDictionary<object, ConcurrentDictionary<CacheKey, object>> readerCache = new WeakDictionary<object, ConcurrentDictionary<CacheKey, object>>(); #endif protected abstract object CreateValue(AtomicReader reader, CacheKey key, bool setDocsWithField); @@ -219,10 +225,10 @@ namespace Lucene.Net.Search /// Remove this reader from the cache, if present. </summary> public virtual void PurgeByCacheKey(object coreCacheKey) { +#if !FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR lock (readerCache) - { +#endif readerCache.Remove(coreCacheKey); - } } /// <summary> @@ -231,79 +237,87 @@ namespace Lucene.Net.Search /// </summary> public virtual void Put(AtomicReader reader, CacheKey key, object value) { + ConcurrentDictionary<CacheKey, object> innerCache; object readerKey = reader.CoreCacheKey; +#if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR + innerCache = readerCache.GetValue(readerKey, (readerKey) => + { + // First time this reader is using FieldCache + wrapper.InitReader(reader); + return new ConcurrentDictionary<CacheKey, object> + { + [key] = value + }; + }); +#else lock (readerCache) { - if (!readerCache.TryGetValue(readerKey, out IDictionary<CacheKey, object> innerCache) || innerCache == null) + if (!readerCache.TryGetValue(readerKey, out innerCache) || innerCache == null) { // First time this reader is using FieldCache - innerCache = new Dictionary<CacheKey, object>(); - readerCache.AddOrUpdate(readerKey, innerCache); + innerCache = new ConcurrentDictionary<CacheKey, object> + { + [key] = value + }; + readerCache.Add(readerKey, innerCache); wrapper.InitReader(reader); } - // LUCENENET NOTE: We declare a temp variable here so we - // don't overwrite value variable with the null - // that will result when this if block succeeds; otherwise - // we won't have a value to put in the cache. - if (!innerCache.TryGetValue(key, out object temp)) - { - innerCache[key] = value; - } - else - { - // Another thread beat us to it; leave the current - // value - } } +#endif + // If another thread beat us to it, leave the current value + innerCache.TryAdd(key, value); } public virtual object Get(AtomicReader reader, CacheKey key, bool setDocsWithField) { - IDictionary<CacheKey, object> innerCache; - object value; + ConcurrentDictionary<CacheKey, object> innerCache; object readerKey = reader.CoreCacheKey; +#if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR + innerCache = readerCache.GetValue(readerKey, (readerKey) => + { + // First time this reader is using FieldCache + wrapper.InitReader(reader); + return new ConcurrentDictionary<CacheKey, object> + { + [key] = new FieldCache.CreationPlaceholder() + }; + }); + +#else lock (readerCache) { if (!readerCache.TryGetValue(readerKey, out innerCache) || innerCache == null) { // First time this reader is using FieldCache - innerCache = new Dictionary<CacheKey, object>(); - readerCache.AddOrUpdate(readerKey, innerCache); + innerCache = new ConcurrentDictionary<CacheKey, object> + { + [key] = new FieldCache.CreationPlaceholder() + }; + readerCache[readerKey] = innerCache; wrapper.InitReader(reader); - value = null; - } - else - { - innerCache.TryGetValue(key, out value); - } - if (value == null) - { - value = new FieldCache.CreationPlaceholder(); - innerCache[key] = value; } } - if (value is FieldCache.CreationPlaceholder) +#endif + object value = innerCache.GetOrAdd(key, (cacheKey) => new FieldCache.CreationPlaceholder()); + if (value is FieldCache.CreationPlaceholder progress) { lock (value) { - FieldCache.CreationPlaceholder progress = (FieldCache.CreationPlaceholder)value; if (progress.Value == null) { progress.Value = CreateValue(reader, key, setDocsWithField); - lock (readerCache) - { - innerCache[key] = progress.Value; - } - - // Only check if key.custom (the parser) is - // non-null; else, we check twice for a single - // call to FieldCache.getXXX - if (key.custom != null && wrapper != null) + if (innerCache.TryUpdate(key, progress.Value, value)) { - TextWriter infoStream = wrapper.InfoStream; - if (infoStream != null) + // Only check if key.custom (the parser) is + // non-null; else, we check twice for a single + // call to FieldCache.getXXX + if (key.custom != null && wrapper != null) { - PrintNewInsanity(infoStream, progress.Value); + TextWriter infoStream = wrapper.InfoStream; + if (infoStream != null) + { + PrintNewInsanity(infoStream, progress.Value); + } } } }
