diff --git a/BitFaster.Caching.UnitTests/Lfu/ConcurrentLfuTests.cs b/BitFaster.Caching.UnitTests/Lfu/ConcurrentLfuTests.cs index 796f6e74..b5d5f4a9 100644 --- a/BitFaster.Caching.UnitTests/Lfu/ConcurrentLfuTests.cs +++ b/BitFaster.Caching.UnitTests/Lfu/ConcurrentLfuTests.cs @@ -672,6 +672,16 @@ public void WhenRemovedInWriteBuffer() public void WhenItemDoesNotExistTryUpdateIsFalse() { cache.TryUpdate(1, 2).Should().BeFalse(); + } + + [Fact] + public void WhenAddingNullValueCanBeAddedAndRemoved() + { + // use foreground so that any null ref exceptions will surface + var lfu = new ConcurrentLfu(1, 20, new ForegroundScheduler(), EqualityComparer.Default); + lfu.GetOrAdd(1, _ => null).Should().BeNull(); + lfu.AddOrUpdate(1, null); + lfu.TryRemove(1).Should().BeTrue(); } [Fact] diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs index ab2bdbc1..5395700e 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs @@ -574,6 +574,13 @@ public void WhenValueExpiresItIsDisposed() disposableValueFactory.Items[6].IsDisposed.Should().BeFalse(); } + [Fact] + public void WhenAddingNullValueCanBeAddedAndRemoved() + { + lru.GetOrAdd(1, _ => null).Should().BeNull(); + lru.AddOrUpdate(1, null); + lru.TryRemove(1).Should().BeTrue(); + } [Fact] public void WhenValueEvictedItemRemovedEventIsFired() diff --git a/BitFaster.Caching/Lru/ConcurrentLruCore.cs b/BitFaster.Caching/Lru/ConcurrentLruCore.cs index acb3cd2e..25558fa1 100644 --- a/BitFaster.Caching/Lru/ConcurrentLruCore.cs +++ b/BitFaster.Caching/Lru/ConcurrentLruCore.cs @@ -359,7 +359,7 @@ private void OnRemove(K key, I item) this.telemetryPolicy.OnItemRemoved(key, item.Value, ItemRemovedReason.Removed); // serialize dispose (common case dispose not thread safe) - lock (item.Value) + lock (item) { Disposer.Dispose(item.Value); }