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 89bad64c3d0058e7e648291db0d13e5de0b9205a Author: Shad Storhaug <[email protected]> AuthorDate: Thu Aug 12 21:30:20 2021 +0700 BUG: Lucene.Net.Index.IndexReader: Use ConditionalWeakTable/WeakDictionary to ensure dead elements are pruned and garbage collected (fixes #506). --- src/Lucene.Net/Index/IndexReader.cs | 29 +++++++++--- src/Lucene.Net/Support/IdentityWeakReference.cs | 59 ------------------------- 2 files changed, 23 insertions(+), 65 deletions(-) diff --git a/src/Lucene.Net/Index/IndexReader.cs b/src/Lucene.Net/Index/IndexReader.cs index d97842f..c75c80b 100644 --- a/src/Lucene.Net/Index/IndexReader.cs +++ b/src/Lucene.Net/Index/IndexReader.cs @@ -100,7 +100,14 @@ namespace Lucene.Net.Index private readonly ISet<IReaderClosedListener> readerClosedListeners = new JCG.LinkedHashSet<IReaderClosedListener>().AsConcurrent(); - private readonly ISet<IdentityWeakReference<IndexReader>> parentReaders = new ConcurrentHashSet<IdentityWeakReference<IndexReader>>(); +#if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR + private readonly ConditionalWeakTable<IndexReader, object> parentReaders = new ConditionalWeakTable<IndexReader, object>(); +#else + private readonly WeakDictionary<IndexReader, object> parentReaders = new WeakDictionary<IndexReader, object>(); +#endif + // LUCENENET specific - since neither WeakDictionary nor ConditionalWeakTable synchronize + // on the enumerator, we need to do external synchronization to make them threadsafe. + private readonly object parentReadersLock = new object(); /// <summary> /// Expert: adds a <see cref="IReaderClosedListener"/>. The @@ -136,7 +143,14 @@ namespace Lucene.Net.Index public void RegisterParentReader(IndexReader reader) { EnsureOpen(); - parentReaders.Add(new IdentityWeakReference<IndexReader>(reader)); + // LUCENENET specific - since neither WeakDictionary nor ConditionalWeakTable synchronize + // on the enumerator, we need to do external synchronization to make them threadsafe. + lock (parentReadersLock) + // LUCENENET: Since there is a set Add operation (unique) in Lucene, the equivalent + // operation in .NET is AddOrUpdate, which effectively does nothing if the key exists. + // Null is passed as a value, since it is not used anyway and .NET doesn't have a boolean + // reference type. + parentReaders.AddOrUpdate(key: reader, value: null); } private void NotifyReaderClosedListeners(Exception th) @@ -167,15 +181,18 @@ namespace Lucene.Net.Index private void ReportCloseToParentReaders() { - lock (parentReaders) // LUCENENET: This does not actually synchronize the set, it only ensures this method can only be entered by 1 thread + // LUCENENET specific - since neither WeakDictionary nor ConditionalWeakTable synchronize + // on the enumerator, we need to do external synchronization to make them threadsafe. + lock (parentReadersLock) { - foreach (IdentityWeakReference<IndexReader> parent in parentReaders) + foreach (var kvp in parentReaders) { - //Using weak references - IndexReader target = parent.Target; + IndexReader target = kvp.Key; + // LUCENENET: This probably can't happen, but we are being defensive to avoid exceptions if (target != null) { + //Using weak references target.closedByChild = true; // cross memory barrier by a fake write: target.refCount.AddAndGet(0); diff --git a/src/Lucene.Net/Support/IdentityWeakReference.cs b/src/Lucene.Net/Support/IdentityWeakReference.cs deleted file mode 100644 index dac12b8..0000000 --- a/src/Lucene.Net/Support/IdentityWeakReference.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace Lucene.Net.Support -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - internal class IdentityWeakReference<T> : WeakReference - where T : class - { - private readonly int hash; - private static readonly object NULL = new object(); - - public IdentityWeakReference(T target) - : base(target ?? NULL) - { - hash = RuntimeHelpers.GetHashCode(target); - } - - public override int GetHashCode() - { - return hash; - } - - public override bool Equals(object o) - { - if (ReferenceEquals(this, o)) - { - return true; - } - if (o is IdentityWeakReference<T> iwr && ReferenceEquals(this.Target, iwr.Target)) - { - return true; - } - return false; - } - - public new T Target - { // note: if this.NULL is the target, it will not cast to T, so the "as" will return null as we would expect. - get => base.Target as T; - set => base.Target = value; - } - } -} \ No newline at end of file
