Skip to content

Commit e09530d

Browse files
authored
extend singletoncache (#25)
1 parent 3bf1bbf commit e09530d

File tree

2 files changed

+90
-76
lines changed

2 files changed

+90
-76
lines changed

BitFaster.Caching/SingletonCache.cs

Lines changed: 75 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,89 +7,88 @@
77

88
namespace BitFaster.Caching
99
{
10-
/// <summary>
11-
/// Cache a single value for each key, and maintain in memory only the values that have been acquired
12-
/// but not yet released.
13-
/// </summary>
14-
/// <typeparam name="TKey">The type of the key.</typeparam>
15-
/// <typeparam name="TValue">The type of the value.</typeparam>
16-
public class SingletonCache<TKey, TValue>
17-
where TValue : new()
18-
{
19-
private readonly ConcurrentDictionary<TKey, ReferenceCount<TValue>> cache;
10+
/// <summary>
11+
/// Cache a single value for each key, and maintain in memory only the values that have been acquired
12+
/// but not yet released.
13+
/// </summary>
14+
/// <typeparam name="TKey">The type of the key.</typeparam>
15+
/// <typeparam name="TValue">The type of the value.</typeparam>
16+
public class SingletonCache<TKey, TValue>
17+
{
18+
private readonly ConcurrentDictionary<TKey, ReferenceCount<TValue>> cache;
2019

21-
public SingletonCache()
22-
{
23-
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>();
24-
}
20+
public SingletonCache()
21+
{
22+
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>();
23+
}
2524

26-
public SingletonCache(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
27-
{
28-
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>(concurrencyLevel, capacity, comparer);
29-
}
25+
public SingletonCache(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
26+
{
27+
this.cache = new ConcurrentDictionary<TKey, ReferenceCount<TValue>>(concurrencyLevel, capacity, comparer);
28+
}
3029

31-
public Handle Acquire(TKey key)
32-
{
33-
var refCount = this.cache.AddOrUpdate(key,
34-
(_) => new ReferenceCount<TValue>(new TValue()),
35-
(_, existingRefCount) => existingRefCount.IncrementCopy());
30+
public Handle Acquire(TKey key, Func<TKey, TValue> valueFactory)
31+
{
32+
var refCount = this.cache.AddOrUpdate(key,
33+
(_) => new ReferenceCount<TValue>(valueFactory(_)),
34+
(_, existingRefCount) => existingRefCount.IncrementCopy());
3635

37-
return new Handle(key, refCount.Value, this);
38-
}
36+
return new Handle(key, refCount.Value, this);
37+
}
3938

40-
private void Release(TKey key)
41-
{
42-
while (true)
43-
{
44-
var oldRefCount = this.cache[key];
45-
var newRefCount = oldRefCount.DecrementCopy();
46-
if (this.cache.TryUpdate(key, newRefCount, oldRefCount))
47-
{
48-
if (newRefCount.Count == 0)
49-
{
50-
// This will remove from dictionary only if key and the value with ReferenceCount (== 0) matches (under a lock)
51-
if (((IDictionary<TKey, ReferenceCount<TValue>>)this.cache).Remove(new KeyValuePair<TKey, ReferenceCount<TValue>>(key, newRefCount)))
52-
{
53-
if (newRefCount.Value is IDisposable d)
54-
{
55-
d.Dispose();
56-
}
57-
}
58-
}
59-
break;
60-
}
61-
}
62-
}
39+
private void Release(TKey key)
40+
{
41+
while (true)
42+
{
43+
var oldRefCount = this.cache[key];
44+
var newRefCount = oldRefCount.DecrementCopy();
45+
if (this.cache.TryUpdate(key, newRefCount, oldRefCount))
46+
{
47+
if (newRefCount.Count == 0)
48+
{
49+
// This will remove from dictionary only if key and the value with ReferenceCount (== 0) matches (under a lock)
50+
if (((IDictionary<TKey, ReferenceCount<TValue>>)this.cache).Remove(new KeyValuePair<TKey, ReferenceCount<TValue>>(key, newRefCount)))
51+
{
52+
if (newRefCount.Value is IDisposable d)
53+
{
54+
d.Dispose();
55+
}
56+
}
57+
}
58+
break;
59+
}
60+
}
61+
}
6362

64-
public sealed class Handle : IDisposable
65-
{
66-
private TKey key;
67-
private TValue value;
68-
private SingletonCache<TKey, TValue> cache;
63+
public sealed class Handle : IDisposable
64+
{
65+
private TKey key;
66+
private TValue value;
67+
private SingletonCache<TKey, TValue> cache;
6968

70-
public Handle(TKey key, TValue value, SingletonCache<TKey, TValue> cache)
71-
{
72-
this.key = key;
73-
this.value = value;
74-
this.cache = cache;
75-
}
69+
public Handle(TKey key, TValue value, SingletonCache<TKey, TValue> cache)
70+
{
71+
this.key = key;
72+
this.value = value;
73+
this.cache = cache;
74+
}
7675

77-
public TValue Value
78-
{
79-
get
80-
{
81-
return this.value;
82-
}
83-
}
76+
public TValue Value
77+
{
78+
get
79+
{
80+
return this.value;
81+
}
82+
}
8483

85-
public void Dispose()
86-
{
87-
if (this.cache != null)
88-
{
89-
this.cache.Release(this.key);
90-
this.cache = null;
91-
}
92-
}
93-
}
94-
}
84+
public void Dispose()
85+
{
86+
if (this.cache != null)
87+
{
88+
this.cache.Release(this.key);
89+
this.cache = null;
90+
}
91+
}
92+
}
93+
}
9594
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace BitFaster.Caching
6+
{
7+
public static class SingletonCacheExtensions
8+
{
9+
public static SingletonCache<TKey, TValue>.Handle Acquire<TKey, TValue>(this SingletonCache<TKey, TValue> cache, TKey key)
10+
where TValue : new()
11+
{
12+
return cache.Acquire(key, _ => new TValue());
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)