using System.Collections; namespace Umbraco.Cms.Core.Collections; /// /// A thread-safe representation of a . /// Enumerating this collection is thread-safe and will only operate on a clone that is generated before returning the /// enumerator. /// /// [Serializable] public class ConcurrentHashSet : ICollection, ISet { private readonly HashSet _innerSet = new(); private readonly ReaderWriterLockSlim _instanceLocker = new(LockRecursionPolicy.NoRecursion); /// /// Gets the number of elements contained in the . /// /// /// The number of elements contained in the . /// /// 2 public int Count { get { try { _instanceLocker.EnterReadLock(); return _innerSet.Count; } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator GetEnumerator() => GetThreadSafeClone().GetEnumerator(); /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Removes the first occurrence of a specific object from the /// . /// /// /// true if was successfully removed from the /// ; otherwise, false. This method also returns false if /// is not found in the original . /// /// The object to remove from the . /// /// The is /// read-only. /// public bool Remove(T item) { try { _instanceLocker.EnterWriteLock(); return _innerSet.Remove(item); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } /// /// Gets a value indicating whether the is read-only. /// /// /// true if the is read-only; otherwise, false. /// public bool IsReadOnly => false; /// /// Adds an item to the . /// /// The object to add to the . /// /// The is /// read-only. /// public void Add(T item) { try { _instanceLocker.EnterWriteLock(); _innerSet.Add(item); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } /// /// Removes all items from the . /// /// /// The is /// read-only. /// public void Clear() { try { _instanceLocker.EnterWriteLock(); _innerSet.Clear(); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } /// /// Determines whether the contains a specific value. /// /// /// true if is found in the ; /// otherwise, false. /// /// The object to locate in the . public bool Contains(T item) { try { _instanceLocker.EnterReadLock(); return _innerSet.Contains(item); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } /// /// Copies the elements of the to an /// , starting at a specified index. /// /// /// The one-dimensional that is the destination of the elements copied /// from the . The array must have /// zero-based indexing. /// /// The zero-based index in at which copying begins. /// /// is a null reference (Nothing in Visual /// Basic). /// /// is less than zero. /// /// is equal to or greater than the length of the /// -or- The number of elements in the source /// is greater than the available space from /// to the end of the destination . /// public void CopyTo(T[] array, int index) { HashSet clone = GetThreadSafeClone(); clone.CopyTo(array, index); } /// /// Attempts to add an item to the collection /// /// /// public bool TryAdd(T item) { if (Contains(item)) { return false; } try { _instanceLocker.EnterWriteLock(); // double check if (_innerSet.Contains(item)) { return false; } _innerSet.Add(item); return true; } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } /// /// Copies the elements of the to an , /// starting at a particular index. /// /// /// The one-dimensional that is the destination of the elements copied /// from . The must have zero-based /// indexing. /// /// The zero-based index in at which copying begins. /// is null. /// is less than zero. /// /// is multidimensional.-or- The number of elements /// in the source is greater than the available space from /// to the end of the destination . /// /// /// The type of the source /// cannot be cast automatically to the type of the destination . /// /// 2 public void CopyTo(Array array, int index) { HashSet clone = GetThreadSafeClone(); Array.Copy(clone.ToArray(), 0, array, index, clone.Count); } private HashSet GetThreadSafeClone() { HashSet? clone = null; try { _instanceLocker.EnterReadLock(); clone = new HashSet(_innerSet, _innerSet.Comparer); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } return clone; } public void ExceptWith(IEnumerable other) { try { _instanceLocker.EnterWriteLock(); _innerSet.ExceptWith(other); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } public void IntersectWith(IEnumerable other) { try { _instanceLocker.EnterWriteLock(); _innerSet.IntersectWith(other); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } public bool IsProperSubsetOf(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.IsProperSubsetOf(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public bool IsProperSupersetOf(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.IsProperSupersetOf(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public bool IsSubsetOf(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.IsSubsetOf(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public bool IsSupersetOf(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.IsSupersetOf(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public bool Overlaps(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.Overlaps(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public bool SetEquals(IEnumerable other) { try { _instanceLocker.EnterReadLock(); return _innerSet.SetEquals(other); } finally { if (_instanceLocker.IsReadLockHeld) { _instanceLocker.ExitReadLock(); } } } public void SymmetricExceptWith(IEnumerable other) { try { _instanceLocker.EnterWriteLock(); _innerSet.IntersectWith(other); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } public void UnionWith(IEnumerable other) { try { _instanceLocker.EnterWriteLock(); _innerSet.UnionWith(other); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } bool ISet.Add(T item) { try { _instanceLocker.EnterWriteLock(); return _innerSet.Add(item); } finally { if (_instanceLocker.IsWriteLockHeld) { _instanceLocker.ExitWriteLock(); } } } }