diff --git a/src/Lucene.Net/Support/ConcurrentHashSet.cs b/src/Lucene.Net/Support/ConcurrentHashSet.cs index 917b3ce356..877e4685e0 100644 --- a/src/Lucene.Net/Support/ConcurrentHashSet.cs +++ b/src/Lucene.Net/Support/ConcurrentHashSet.cs @@ -74,9 +74,9 @@ public int Count { AcquireAllLocks(ref acquiredLocks); - for (var i = 0; i < _tables.CountPerLock.Length; i++) + foreach (var t in _tables.CountPerLock) { - count += _tables.CountPerLock[i]; + count += t; } } finally @@ -88,6 +88,21 @@ public int Count } } + private int CountInternal + { + get + { + int count = 0; + + foreach (var t in _tables.CountPerLock) + { + count += t; + } + + return count; + } + } + /// /// Gets a value that indicates whether the is empty. /// @@ -298,9 +313,7 @@ public void Clear() { AcquireAllLocks(ref locksAcquired); - var newTables = new Tables(new Node[DefaultCapacity], _tables.Locks, new int[_tables.CountPerLock.Length]); - _tables = newTables; - _budget = Math.Max(1, newTables.Buckets.Length / newTables.Locks.Length); + ClearInternal(); } finally { @@ -308,6 +321,13 @@ public void Clear() } } + private void ClearInternal() + { + var newTables = new Tables(new Node[DefaultCapacity], _tables.Locks, new int[_tables.CountPerLock.Length]); + _tables = newTables; + _budget = Math.Max(1, newTables.Buckets.Length / newTables.Locks.Length); + } + /// /// Determines whether the contains the specified /// item. @@ -347,6 +367,11 @@ public bool Contains(T item) public bool TryRemove(T item) { var hashcode = _comparer.GetHashCode(item); + return TryRemoveInternal(item, hashcode, acquireLock: true); + } + + private bool TryRemoveInternal(T item, int hashcode, bool acquireLock) + { while (true) { var tables = _tables; @@ -354,9 +379,13 @@ public bool TryRemove(T item) GetBucketAndLockNo(hashcode, out int bucketNo, out int lockNo, tables.Buckets.Length, tables.Locks.Length); object syncRoot = tables.Locks[lockNo]; - UninterruptableMonitor.Enter(syncRoot); + var lockTaken = false; + try { + if (acquireLock) + UninterruptableMonitor.Enter(syncRoot, ref lockTaken); + // If the table just got resized, we may not be holding the right lock, and must retry. // This should be a rare occurrence. if (tables != _tables) @@ -388,7 +417,8 @@ public bool TryRemove(T item) } finally { - UninterruptableMonitor.Exit(syncRoot); + if (lockTaken) + UninterruptableMonitor.Exit(syncRoot); } return false; @@ -753,15 +783,33 @@ private void CopyToItems(T[] array, int index) public void ExceptWith(IEnumerable other) { - if (ReferenceEquals(this, other)) + if (other is null) + throw new ArgumentNullException(nameof(other)); + + var locksAcquired = 0; + try { - Clear(); - return; - } + AcquireAllLocks(ref locksAcquired); + + if (CountInternal == 0) + { + return; + } + + if (ReferenceEquals(this, other)) + { + ClearInternal(); + return; + } - foreach (var item in other) + foreach (var item in other) + { + TryRemoveInternal(item, _comparer.GetHashCode(item), acquireLock: false); + } + } + finally { - TryRemove(item); + ReleaseLocks(0, locksAcquired); } }