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 1fe3b485e6210ab440e82e8e486dbbd0368928cb Author: Shad Storhaug <[email protected]> AuthorDate: Fri Aug 13 01:58:38 2021 +0700 Lucene.Net.Support.WeakDictionary: Changed WeakKey to use WeakReference<T> instead of WeakReference to avoid problems with garbage collection (see #506). --- src/Lucene.Net/Support/WeakDictionary.cs | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/Lucene.Net/Support/WeakDictionary.cs b/src/Lucene.Net/Support/WeakDictionary.cs index 12673ad..1633499 100644 --- a/src/Lucene.Net/Support/WeakDictionary.cs +++ b/src/Lucene.Net/Support/WeakDictionary.cs @@ -19,12 +19,10 @@ * */ - #if !FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using JCG = J2N.Collections.Generic; namespace Lucene.Net.Support @@ -66,9 +64,16 @@ namespace Lucene.Net.Support { if (_hm.Count == 0) return; var newHm = new JCG.Dictionary<WeakKey<TKey>, TValue>(_hm.Count); - foreach (var entry in _hm.Where(x => x.Key != null && x.Key.IsAlive)) + foreach (var kvp in _hm) { - newHm.Add(entry.Key, entry.Value); + if (kvp.Key.TryGetTarget(out TKey _)) + { + // LUCENENET: There is a tiny chance that a call to remove the item + // from the dictionary can happen before this line is executed. Therefore, + // just discard the reference and add it as is, even if it is no longer valid + // in this edge case. It is far more efficient to re-use the same instances, anyway. + newHm.Add(kvp.Key, kvp.Value); + } } _hm = newHm; } @@ -85,9 +90,12 @@ namespace Lucene.Net.Support public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { - foreach (var kvp in _hm.Where(x => x.Key.IsAlive)) + foreach (var kvp in _hm) { - yield return new KeyValuePair<TKey, TValue>(kvp.Key.Target, kvp.Value); + if (kvp.Key.TryGetTarget(out TKey key)) + { + yield return new KeyValuePair<TKey, TValue>(key, kvp.Value); + } } } @@ -185,7 +193,7 @@ namespace Lucene.Net.Support throw new NotSupportedException(); } -#region KeyCollection + #region Nested Class: KeyCollection private class KeyCollection : ICollection<TKey> { @@ -200,7 +208,8 @@ namespace Lucene.Net.Support { foreach (var key in _internalDict.Keys) { - yield return key.Target; + if (key.TryGetTarget(out TKey target)) + yield return target; } } @@ -243,7 +252,7 @@ namespace Lucene.Net.Support #endregion Explicit Interface Definitions } -#endregion KeyCollection + #endregion Nested Class: KeyCollection /// <summary> /// A weak reference wrapper for the hashtable keys. Whenever a key\value pair @@ -252,7 +261,7 @@ namespace Lucene.Net.Support /// </summary> private class WeakKey<T> where T : class { - private readonly WeakReference reference; + private readonly WeakReference<T> reference; private readonly int hashCode; public WeakKey(T key) @@ -260,8 +269,8 @@ namespace Lucene.Net.Support if (key is null) throw new ArgumentNullException(nameof(key)); - hashCode = key.GetHashCode(); - reference = new WeakReference(key); + hashCode = key is null ? 0 : key.GetHashCode(); + reference = new WeakReference<T>(key); } public override int GetHashCode() @@ -271,25 +280,20 @@ namespace Lucene.Net.Support public override bool Equals(object obj) { - if (!reference.IsAlive || obj is null) return false; - - if (object.ReferenceEquals(this, obj)) - { - return true; - } - + if (obj is null) return false; if (obj is WeakKey<T> other) { - var referenceTarget = reference.Target; // Careful: can be null in the mean time... - return referenceTarget != null && referenceTarget.Equals(other.Target); + bool gotThis = this.TryGetTarget(out T thisTarget), gotOther = other.TryGetTarget(out T otherTarget); + if (gotThis && gotOther && thisTarget.Equals(otherTarget)) + return true; + else if (gotThis == false && gotOther == false) + return true; } return false; } - public T Target => (T)reference.Target; - - public bool IsAlive => reference.IsAlive; + public bool TryGetTarget(out T target) => reference.TryGetTarget(out target); } } }
