diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs index d102b22a7c714..bf33bbeee67cf 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs @@ -390,52 +390,57 @@ private bool TryRemoveInternal(TKey key, [MaybeNullWhen(false)] out TValue value object[] locks = tables._locks; ref Node? bucket = ref GetBucketAndLock(tables, hashcode, out uint lockNo); - lock (locks[lockNo]) + // Do a hot read on number of items stored in the bucket. If it's empty, we can avoid + // taking the lock and fail fast. + if (tables._countPerLock[lockNo] != 0) { - // 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) + lock (locks[lockNo]) { - tables = _tables; - if (!ReferenceEquals(comparer, tables._comparer)) + // 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) { - comparer = tables._comparer; - hashcode = GetHashCode(comparer, key); + tables = _tables; + if (!ReferenceEquals(comparer, tables._comparer)) + { + comparer = tables._comparer; + hashcode = GetHashCode(comparer, key); + } + continue; } - continue; - } - Node? prev = null; - for (Node? curr = bucket; curr is not null; curr = curr._next) - { - Debug.Assert((prev is null && curr == bucket) || prev!._next == curr); - - if (hashcode == curr._hashcode && NodeEqualsKey(comparer, curr, key)) + Node? prev = null; + for (Node? curr = bucket; curr is not null; curr = curr._next) { - if (matchValue) + Debug.Assert((prev is null && curr == bucket) || prev!._next == curr); + + if (hashcode == curr._hashcode && NodeEqualsKey(comparer, curr, key)) { - bool valuesMatch = EqualityComparer.Default.Equals(oldValue, curr._value); - if (!valuesMatch) + if (matchValue) { - value = default; - return false; + bool valuesMatch = EqualityComparer.Default.Equals(oldValue, curr._value); + if (!valuesMatch) + { + value = default; + return false; + } } - } - if (prev is null) - { - Volatile.Write(ref bucket, curr._next); - } - else - { - prev._next = curr._next; - } + if (prev is null) + { + Volatile.Write(ref bucket, curr._next); + } + else + { + prev._next = curr._next; + } - value = curr._value; - tables._countPerLock[lockNo]--; - return true; + value = curr._value; + tables._countPerLock[lockNo]--; + return true; + } + prev = curr; } - prev = curr; } }