Skip to content

Commit 0fbf72e

Browse files
author
Alex Peck
committed
repro and fix
1 parent f9563f8 commit 0fbf72e

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

BitFaster.Caching.UnitTests/Lru/ConcurrentLruSoakTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,31 @@ await Threaded.Run(4, () =>
237237
cache.Metrics.Value.Evicted.Should().Be(0);
238238
}
239239

240+
[Fact]
241+
public async Task WhenConcurrentUpdateAndRemoveKvp()
242+
{
243+
TaskCompletionSource<int> tcs = new TaskCompletionSource<int> ();
244+
245+
var removal = Task.Run(() =>
246+
{
247+
while (!tcs.Task.IsCompleted)
248+
{
249+
lru.TryRemove(new KeyValuePair<int, string>(5, "x"));
250+
}
251+
});
252+
253+
for (var i = 0; i < 100_000; i++)
254+
{
255+
lru.AddOrUpdate(5, "a");
256+
lru.TryGet(5, out _).Should().BeTrue("key 'a' should not be deleted");
257+
lru.AddOrUpdate(5, "x");
258+
}
259+
260+
tcs.SetResult(int.MaxValue);
261+
262+
await removal;
263+
}
264+
240265
[Theory]
241266
[Repeat(10)]
242267
public async Task WhenConcurrentGetAndClearCacheEndsInConsistentState(int iteration)

BitFaster.Caching/Lru/ConcurrentLruCore.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -308,18 +308,21 @@ public bool TryRemove(KeyValuePair<K, V> item)
308308
{
309309
if (this.dictionary.TryGetValue(item.Key, out var existing))
310310
{
311-
if (EqualityComparer<V>.Default.Equals(existing.Value, item.Value))
311+
lock (existing)
312312
{
313-
var kvp = new KeyValuePair<K, I>(item.Key, existing);
313+
if (EqualityComparer<V>.Default.Equals(existing.Value, item.Value))
314+
{
315+
var kvp = new KeyValuePair<K, I>(item.Key, existing);
314316
#if NET6_0_OR_GREATER
315317
if (this.dictionary.TryRemove(kvp))
316318
#else
317-
// https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/
318-
if (((ICollection<KeyValuePair<K, I>>)this.dictionary).Remove(kvp))
319+
// https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/
320+
if (((ICollection<KeyValuePair<K, I>>)this.dictionary).Remove(kvp))
319321
#endif
320-
{
321-
OnRemove(item.Key, kvp.Value, ItemRemovedReason.Removed);
322-
return true;
322+
{
323+
OnRemove(item.Key, kvp.Value, ItemRemovedReason.Removed);
324+
return true;
325+
}
323326
}
324327
}
325328

0 commit comments

Comments
 (0)