Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 75 additions & 76 deletions BitFaster.Caching/SingletonCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,88 @@

namespace BitFaster.Caching
{
/// <summary>
/// Cache a single value for each key, and maintain in memory only the values that have been acquired
/// but not yet released.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
public class SingletonCache<TKey, TValue>
where TValue : new()
{
private readonly ConcurrentDictionary<TKey, ReferenceCount<TValue>> cache;
/// <summary>
/// Cache a single value for each key, and maintain in memory only the values that have been acquired
/// but not yet released.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
public class SingletonCache<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, ReferenceCount<TValue>> cache;

public SingletonCache()
{
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>();
}
public SingletonCache()
{
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>();
}

public SingletonCache(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
{
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>(concurrencyLevel, capacity, comparer);
}
public SingletonCache(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
{
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>(concurrencyLevel, capacity, comparer);
}

public Handle Acquire(TKey key)
{
var refCount = this.cache.AddOrUpdate(key,
(_) => new ReferenceCount<TValue>(new TValue()),
(_, existingRefCount) => existingRefCount.IncrementCopy());
public Handle Acquire(TKey key, Func<TKey, TValue> valueFactory)
{
var refCount = this.cache.AddOrUpdate(key,
(_) => new ReferenceCount<TValue>(valueFactory(_)),
(_, existingRefCount) => existingRefCount.IncrementCopy());

return new Handle(key, refCount.Value, this);
}
return new Handle(key, refCount.Value, this);
}

private void Release(TKey key)
{
while (true)
{
var oldRefCount = this.cache[key];
var newRefCount = oldRefCount.DecrementCopy();
if (this.cache.TryUpdate(key, newRefCount, oldRefCount))
{
if (newRefCount.Count == 0)
{
// This will remove from dictionary only if key and the value with ReferenceCount (== 0) matches (under a lock)
if (((IDictionary<TKey, ReferenceCount<TValue>>)this.cache).Remove(new KeyValuePair<TKey, ReferenceCount<TValue>>(key, newRefCount)))
{
if (newRefCount.Value is IDisposable d)
{
d.Dispose();
}
}
}
break;
}
}
}
private void Release(TKey key)
{
while (true)
{
var oldRefCount = this.cache[key];
var newRefCount = oldRefCount.DecrementCopy();
if (this.cache.TryUpdate(key, newRefCount, oldRefCount))
{
if (newRefCount.Count == 0)
{
// This will remove from dictionary only if key and the value with ReferenceCount (== 0) matches (under a lock)
if (((IDictionary<TKey, ReferenceCount<TValue>>)this.cache).Remove(new KeyValuePair<TKey, ReferenceCount<TValue>>(key, newRefCount)))
{
if (newRefCount.Value is IDisposable d)
{
d.Dispose();
}
}
}
break;
}
}
}

public sealed class Handle : IDisposable
{
private TKey key;
private TValue value;
private SingletonCache<TKey, TValue> cache;
public sealed class Handle : IDisposable
{
private TKey key;
private TValue value;
private SingletonCache<TKey, TValue> cache;

public Handle(TKey key, TValue value, SingletonCache<TKey, TValue> cache)
{
this.key = key;
this.value = value;
this.cache = cache;
}
public Handle(TKey key, TValue value, SingletonCache<TKey, TValue> cache)
{
this.key = key;
this.value = value;
this.cache = cache;
}

public TValue Value
{
get
{
return this.value;
}
}
public TValue Value
{
get
{
return this.value;
}
}

public void Dispose()
{
if (this.cache != null)
{
this.cache.Release(this.key);
this.cache = null;
}
}
}
}
public void Dispose()
{
if (this.cache != null)
{
this.cache.Release(this.key);
this.cache = null;
}
}
}
}
}
15 changes: 15 additions & 0 deletions BitFaster.Caching/SingletonCacheExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace BitFaster.Caching
{
public static class SingletonCacheExtensions
{
public static SingletonCache<TKey, TValue>.Handle Acquire<TKey, TValue>(this SingletonCache<TKey, TValue> cache, TKey key)
where TValue : new()
{
return cache.Acquire(key, _ => new TValue());
}
}
}