using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading; namespace Umbraco.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 { private readonly HashSet _innerSet = new HashSet(); private readonly ReaderWriterLockSlim _instanceLocker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator GetEnumerator() { return 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() { return 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 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(); } } } /// /// 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(); } } /// /// 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(); } } /// /// 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) { var clone = GetThreadSafeClone(); clone.CopyTo(array, index); } private HashSet GetThreadSafeClone() { HashSet clone = null; try { _instanceLocker.EnterReadLock(); clone = new HashSet(_innerSet, _innerSet.Comparer); } finally { if (_instanceLocker.IsReadLockHeld) _instanceLocker.ExitReadLock(); } return clone; } /// /// 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) { var clone = GetThreadSafeClone(); Array.Copy(clone.ToArray(), 0, array, index, clone.Count); } } }