diff --git a/BitFaster.Caching.UnitTests/SingletonCacheTests.cs b/BitFaster.Caching.UnitTests/SingletonCacheTests.cs index b3a77d8a..5018aaf8 100644 --- a/BitFaster.Caching.UnitTests/SingletonCacheTests.cs +++ b/BitFaster.Caching.UnitTests/SingletonCacheTests.cs @@ -9,27 +9,27 @@ namespace BitFaster.Caching.UnitTests public class SingletonCacheTests { [Fact] - public void AcquireWithSameKeyUsingCustomComparerReturnsSameHandle() + public void AcquireWithSameKeyUsingCustomComparerReturnsSameLifetime() { var cache = new SingletonCache(1, 3, StringComparer.OrdinalIgnoreCase); - var handle1 = cache.Acquire("foo"); - var handle2 = cache.Acquire("FOO"); - handle1.Value.Should().BeSameAs(handle2.Value); - handle1.Dispose(); - handle2.Dispose(); + var lifetime1 = cache.Acquire("foo"); + var lifetime2 = cache.Acquire("FOO"); + lifetime1.Value.Should().BeSameAs(lifetime2.Value); + lifetime1.Dispose(); + lifetime2.Dispose(); } [Fact] - public void AcquireWithSameKeyReturnsSameHandle() + public void AcquireWithSameKeyReturnsSameLifetime() { var cache = new SingletonCache(); - var handle1 = cache.Acquire("Foo"); - var handle2 = cache.Acquire("Foo"); - handle1.Value.Should().BeSameAs(handle2.Value); - handle1.Dispose(); - handle2.Dispose(); + var lifetime1 = cache.Acquire("Foo"); + var lifetime2 = cache.Acquire("Foo"); + lifetime1.Value.Should().BeSameAs(lifetime2.Value); + lifetime1.Dispose(); + lifetime2.Dispose(); } [Fact] @@ -37,13 +37,13 @@ public void AcquireReleaseAcquireReturnsDifferentValue() { var cache = new SingletonCache(); - var handle1 = cache.Acquire("Foo"); - handle1.Dispose(); + var lifetime1 = cache.Acquire("Foo"); + lifetime1.Dispose(); - var handle2 = cache.Acquire("Foo"); - handle2.Dispose(); + var lifetime2 = cache.Acquire("Foo"); + lifetime2.Dispose(); - handle1.Value.Should().NotBeSameAs(handle2.Value); + lifetime1.Value.Should().NotBeSameAs(lifetime2.Value); } [Fact] @@ -54,17 +54,17 @@ public async Task AcquireWithSameKeyOnTwoDifferentThreadsReturnsSameValue() EventWaitHandle event1 = new EventWaitHandle(false, EventResetMode.AutoReset); EventWaitHandle event2 = new EventWaitHandle(false, EventResetMode.AutoReset); - SingletonCache.Handle handle1 = null; - SingletonCache.Handle handle2 = null; + Lifetime lifetime1 = null; + Lifetime lifetime2 = null; Task task1 = Task.Run(() => { event1.WaitOne(); - handle1 = cache.Acquire("Foo"); + lifetime1 = cache.Acquire("Foo"); event2.Set(); event1.WaitOne(); - handle1.Dispose(); + lifetime1.Dispose(); event2.Set(); }); @@ -72,16 +72,16 @@ public async Task AcquireWithSameKeyOnTwoDifferentThreadsReturnsSameValue() { event1.Set(); event2.WaitOne(); - handle2 = cache.Acquire("Foo"); + lifetime2 = cache.Acquire("Foo"); event1.Set(); event2.WaitOne(); - handle2.Dispose(); + lifetime2.Dispose(); }); await Task.WhenAll(task1, task2); - handle1.Value.Should().BeSameAs(handle2.Value); + lifetime1.Value.Should().BeSameAs(lifetime2.Value); } [Fact] @@ -99,9 +99,9 @@ public async Task AcquireWithSameKeyOnManyDifferentThreadsReturnsSameValue() { for (int i = 0; i < 100000; i++) { - using (var handle = cache.Acquire("Foo")) + using (var lifetime = cache.Acquire("Foo")) { - lock (handle.Value) + lock (lifetime.Value) { int result = Interlocked.Increment(ref count); result.Should().Be(1); @@ -120,7 +120,7 @@ public void WhenValueIsDisposableItIsDisposedWhenReleased() { var cache = new SingletonCache(); - using (var handle = cache.Acquire("Foo")) + using (var lifetime = cache.Acquire("Foo")) { DisposeTest.WasDisposed.Should().BeFalse(); } diff --git a/BitFaster.Caching/Lifetime.cs b/BitFaster.Caching/Lifetime.cs new file mode 100644 index 00000000..93e10058 --- /dev/null +++ b/BitFaster.Caching/Lifetime.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BitFaster.Caching +{ + public class Lifetime : IDisposable + { + private readonly Action onDisposeAction; + private bool isDisposed; + + public Lifetime(T value, Action onDisposeAction) + { + this.Value = value; + this.onDisposeAction = onDisposeAction; + } + + public T Value { get; } + + public void Dispose() + { + if (!this.isDisposed) + { + this.onDisposeAction(); + this.isDisposed = true; + } + } + } +} diff --git a/BitFaster.Caching/Scoped.cs b/BitFaster.Caching/Scoped.cs index 982b3c5c..ad552780 100644 --- a/BitFaster.Caching/Scoped.cs +++ b/BitFaster.Caching/Scoped.cs @@ -21,7 +21,7 @@ public Scoped(T value) this.refCount = new ReferenceCount(value); } - public Lifetime CreateLifetime() + public Lifetime CreateLifetime() { if (this.isDisposed) { @@ -38,7 +38,7 @@ public Lifetime CreateLifetime() if (oldRefCount == Interlocked.CompareExchange(ref this.refCount, newRefCount, oldRefCount)) { // When Lease is disposed, it calls DecrementReferenceCount - return new Lifetime(oldRefCount.Value, this.DecrementReferenceCount); + return new Lifetime(oldRefCount.Value, this.DecrementReferenceCount); } } } @@ -70,28 +70,5 @@ public void Dispose() this.isDisposed = true; } } - - public class Lifetime : IDisposable - { - private readonly Action onDisposeAction; - private bool isDisposed; - - public Lifetime(T value, Action onDisposeAction) - { - this.Value = value; - this.onDisposeAction = onDisposeAction; - } - - public T Value { get; } - - public void Dispose() - { - if (!this.isDisposed) - { - this.onDisposeAction(); - this.isDisposed = true; - } - } - } } } diff --git a/BitFaster.Caching/SingletonCache.cs b/BitFaster.Caching/SingletonCache.cs index 4baeb620..a275d1a5 100644 --- a/BitFaster.Caching/SingletonCache.cs +++ b/BitFaster.Caching/SingletonCache.cs @@ -27,13 +27,13 @@ public SingletonCache(int concurrencyLevel, int capacity, IEqualityComparer>(concurrencyLevel, capacity, comparer); } - public Handle Acquire(TKey key, Func valueFactory) + public Lifetime Acquire(TKey key, Func valueFactory) { var refCount = this.cache.AddOrUpdate(key, (_) => new ReferenceCount(valueFactory(_)), (_, existingRefCount) => existingRefCount.IncrementCopy()); - return new Handle(key, refCount.Value, this); + return new Lifetime(refCount.Value, () => this.Release(key)); } private void Release(TKey key) @@ -46,7 +46,6 @@ private void Release(TKey key) { if (newRefCount.Count == 0) { - // This will remove from dictionary only if key and the value with ReferenceCount (== 0) matches (under a lock) if (((IDictionary>)this.cache).Remove(new KeyValuePair>(key, newRefCount))) { if (newRefCount.Value is IDisposable d) @@ -59,36 +58,5 @@ private void Release(TKey key) } } } - - public sealed class Handle : IDisposable - { - private TKey key; - private TValue value; - private SingletonCache cache; - - public Handle(TKey key, TValue value, SingletonCache cache) - { - this.key = key; - this.value = value; - this.cache = cache; - } - - public TValue Value - { - get - { - return this.value; - } - } - - public void Dispose() - { - if (this.cache != null) - { - this.cache.Release(this.key); - this.cache = null; - } - } - } } } diff --git a/BitFaster.Caching/SingletonCacheExtensions.cs b/BitFaster.Caching/SingletonCacheExtensions.cs index 7d4ca461..81124e85 100644 --- a/BitFaster.Caching/SingletonCacheExtensions.cs +++ b/BitFaster.Caching/SingletonCacheExtensions.cs @@ -6,7 +6,7 @@ namespace BitFaster.Caching { public static class SingletonCacheExtensions { - public static SingletonCache.Handle Acquire(this SingletonCache cache, TKey key) + public static Lifetime Acquire(this SingletonCache cache, TKey key) where TValue : new() { return cache.Acquire(key, _ => new TValue());