using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; namespace NitroxModel.DataStructures { [DebuggerDisplay($"Items = {{{nameof(set)}}}")] [DataContract] [Serializable] public class ThreadSafeSet : ISet { /// /// Using a lock object instead of ReaderWriterLockSlim because to overhead of the latter is ~5x and /// we don't run long write operations anywhere in this class. /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] [IgnoreDataMember] private readonly object locker = new(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] [DataMember(Order = 1)] private readonly HashSet set; public ThreadSafeSet() { set = new HashSet(); } public ThreadSafeSet(HashSet set, bool createCopy = true) { if (set == null || createCopy) { this.set = CreateCopy(set); return; } this.set = set; } public ThreadSafeSet(params T[] values) { set = new HashSet(values); } public int Count { get { lock (locker) { return set.Count; } } } public bool IsReadOnly => false; void ICollection.Add(T item) => Add(item); public bool Add(T item) { lock (locker) { return set.Add(item); } } public void UnionWith(IEnumerable other) { lock (locker) { set.UnionWith(other); } } public void IntersectWith(IEnumerable other) { lock (locker) { set.IntersectWith(other); } } public void ExceptWith(IEnumerable other) { lock (locker) { set.ExceptWith(other); } } public void SymmetricExceptWith(IEnumerable other) { lock (locker) { set.SymmetricExceptWith(other); } } public bool IsSubsetOf(IEnumerable other) { lock (locker) { return set.IsSubsetOf(other); } } public bool IsSupersetOf(IEnumerable other) { lock (locker) { return set.IsSupersetOf(other); } } public bool IsProperSupersetOf(IEnumerable other) { lock (locker) { return set.IsProperSupersetOf(other); } } public bool IsProperSubsetOf(IEnumerable other) { lock (locker) { return set.IsProperSubsetOf(other); } } public bool Overlaps(IEnumerable other) { lock (locker) { return set.Overlaps(other); } } public bool SetEquals(IEnumerable other) { lock (locker) { return set.SetEquals(other); } } bool ISet.Add(T item) { lock (locker) { return set.Add(item); } } public void Clear() { lock (locker) { set.Clear(); } } public void CopyTo(T[] array, int arrayIndex) { lock (locker) { set.CopyTo(array, arrayIndex); } } public bool Remove(T item) { lock (locker) { return set.Remove(item); } } public bool Contains(T item) { lock (locker) { return set.Contains(item); } } public IEnumerator GetEnumerator() { lock (locker) { return CreateCopy(set).GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public List ToList() { lock (locker) { return new List(set); } } /// /// Clears the set and adds the given items. /// /// Items to add onto the empty set. public void Set(IEnumerable items) { lock (locker) { set.Clear(); foreach (T item in items) { set.Add(item); } } } public void RemoveAll(Predicate predicate) { lock (locker) { set.RemoveWhere(predicate); } } public IEnumerable Clone() { lock (locker) { return CreateCopy(set); } } private HashSet CreateCopy(ISet data) { return new HashSet(data); } } }