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();
}
}
}
}