// Needed for Workaround #if !NET_4_6 using System; using System.Collections.Generic; using System.Threading; using LinqInternal.Threading; using LinqInternal.Threading.Needles; namespace LinqInternal.Collections.ThreadSafe { [System.Diagnostics.DebuggerNonUserCode] [System.Diagnostics.DebuggerDisplay("Count={Count}")] internal class WeakCollection : ICollection where T : class where TNeedle : WeakNeedle { private readonly IEqualityComparer _comparer; private readonly SafeDictionary _wrapped; private StructNeedle> _eventHandler; private int _maxIndex; public WeakCollection() : this(null, true) { // Empty } public WeakCollection(IEqualityComparer comparer) : this(comparer, true) { // Empty } public WeakCollection(bool autoRemoveDeadItems) : this(null, autoRemoveDeadItems) { // Empty } public WeakCollection(IEqualityComparer comparer, bool autoRemoveDeadItems) { _maxIndex = -1; _comparer = comparer ?? EqualityComparer.Default; _wrapped = new SafeDictionary(); if (autoRemoveDeadItems) { RegisterForAutoRemoveDeadItemsExtracted(); } else { GC.SuppressFinalize(this); } } public WeakCollection(IEqualityComparer comparer, int initialProbing) : this(comparer, true, initialProbing) { // Empty } public WeakCollection(bool autoRemoveDeadItems, int initialProbing) : this(null, autoRemoveDeadItems, initialProbing) { // Empty } public WeakCollection(IEqualityComparer comparer, bool autoRemoveDeadItems, int initialProbing) { _maxIndex = -1; #if FAT _comparer = comparer ?? EqualityComparer.Default; #else _comparer = comparer ?? EqualityComparer.Default; #endif _wrapped = new SafeDictionary(initialProbing); if (autoRemoveDeadItems) { RegisterForAutoRemoveDeadItemsExtracted(); } else { GC.SuppressFinalize(this); } } public WeakCollection(int initialProbing) : this(null, true, initialProbing) { // Empty } ~WeakCollection() { UnRegisterForAutoRemoveDeadItemsExtracted(); } public bool AutoRemoveDeadItems { get { return _eventHandler.IsAlive; } set { if (value) { RegisterForAutoRemoveDeadItems(); } else { UnRegisterForAutoRemoveDeadItems(); } } } public int Count { get { return _wrapped.Count; } } bool ICollection.IsReadOnly { get { return false; } } public void Add(T item) { var needle = NeedleHelper.CreateNeedle(item); _wrapped.Set(Interlocked.Increment(ref _maxIndex), needle); } public void Clear() { var displaced = _wrapped.ClearEnumerable(); foreach (var item in displaced) { item.Value.Dispose(); } } public bool Contains(T item) { foreach (var input in this) { if (_comparer.Equals(input, item)) { return true; } } return false; } public bool Contains(Predicate itemCheck) { foreach (var input in this) { if (itemCheck(input)) { return true; } } return false; } public void CopyTo(T[] array, int arrayIndex) { Extensions.CopyTo(this, array, arrayIndex); } public bool Equals(T x, T y) { return _comparer.Equals(x, y); } public IEnumerator GetEnumerator() { foreach (var pair in _wrapped) { T result; if (pair.Value.TryGetValue(out result)) { yield return result; } } } public bool Remove(T item) { Predicate check = input => { T value; if (input.TryGetValue(out value)) { return _comparer.Equals(item, value); } return false; }; foreach (var removed in _wrapped.RemoveWhereValueEnumerable(check)) { removed.Dispose(); return true; } return false; } public int RemoveDeadItems() { return _wrapped.RemoveWhere(input => !input.Value.IsAlive); } public int RemoveWhere(Predicate itemCheck) { Predicate check = input => { T value; if (input.TryGetValue(out value)) { return itemCheck(value); } return false; }; return _wrapped.RemoveWhereValue(check); } public IEnumerable RemoveWhereEnumerable(Predicate itemCheck) { Predicate check = input => { T value; if (input.TryGetValue(out value)) { return itemCheck(value); } return false; }; foreach (var removed in _wrapped.RemoveWhereValueEnumerable(check)) { T value; if (removed.TryGetValue(out value)) { yield return value; } removed.Dispose(); } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } protected void Add(TNeedle needle) { _wrapped.Set(Interlocked.Increment(ref _maxIndex), needle); } protected bool Contains(Predicate needleCheck) { foreach (var pair in _wrapped) { if (needleCheck(pair.Value)) { return true; } } return false; } protected IEnumerable GetNeedleEnumerable() { foreach (var pair in _wrapped) { yield return pair.Value; } } protected IEnumerable RemoveWhereEnumerable(Predicate needleCheck) { foreach (var removed in _wrapped.RemoveWhereValueEnumerable(needleCheck)) { T value; if (removed.TryGetValue(out value)) { yield return value; } removed.Dispose(); } } private void GarbageCollected(object sender, EventArgs e) { RemoveDeadItems(); } private void RegisterForAutoRemoveDeadItems() { if (RegisterForAutoRemoveDeadItemsExtracted()) { GC.ReRegisterForFinalize(this); } } private bool RegisterForAutoRemoveDeadItemsExtracted() { var result = false; EventHandler eventHandler; if (ReferenceEquals(_eventHandler.Value, null)) { eventHandler = GarbageCollected; _eventHandler = new WeakNeedle(eventHandler); result = true; } else { eventHandler = _eventHandler.Value.Value; if (!_eventHandler.IsAlive) { eventHandler = GarbageCollected; _eventHandler.Value = eventHandler; result = true; } } GCMonitor.Collected += eventHandler; return result; } private void UnRegisterForAutoRemoveDeadItems() { if (UnRegisterForAutoRemoveDeadItemsExtracted()) { GC.SuppressFinalize(this); } } private bool UnRegisterForAutoRemoveDeadItemsExtracted() { EventHandler eventHandler; if (_eventHandler.Value.Retrieve(out eventHandler)) { GCMonitor.Collected -= eventHandler; _eventHandler.Value = null; return true; } _eventHandler.Value = null; return false; } } } #endif