Skip to content

Commit 498359d

Browse files
authored
fix classic dispose (#23)
1 parent cf3a471 commit 498359d

File tree

11 files changed

+480
-209
lines changed

11 files changed

+480
-209
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Columns;
3+
using BenchmarkDotNet.Configs;
4+
using BenchmarkDotNet.Jobs;
5+
using BitFaster.Caching.Lru;
6+
using System;
7+
using System.Collections.Concurrent;
8+
using System.Collections.Generic;
9+
using System.Runtime.Caching;
10+
using System.Text;
11+
12+
namespace BitFaster.Caching.Benchmarks.Lru
13+
{
14+
[MemoryDiagnoser]
15+
public class LruCycle2
16+
{
17+
const int capacity = 9;
18+
const int iters = 10;
19+
20+
private static readonly ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>(8, 9, EqualityComparer<int>.Default);
21+
22+
private static readonly ClassicLru<int, int> classicLru = new ClassicLru<int, int>(8, capacity, EqualityComparer<int>.Default);
23+
private static readonly ConcurrentLru<int, int> concurrentLru = new ConcurrentLru<int, int>(8, capacity, EqualityComparer<int>.Default);
24+
private static readonly ConcurrentTLru<int, int> concurrentTLru = new ConcurrentTLru<int, int>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));
25+
private static readonly FastConcurrentLru<int, int> fastConcurrentLru = new FastConcurrentLru<int, int>(8, capacity, EqualityComparer<int>.Default);
26+
private static readonly FastConcurrentTLru<int, int> fastConcurrentTLru = new FastConcurrentTLru<int, int>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));
27+
28+
private static MemoryCache memoryCache = System.Runtime.Caching.MemoryCache.Default;
29+
30+
[Benchmark(Baseline = true, OperationsPerInvoke = 24)]
31+
public void ConcurrentDictionary()
32+
{
33+
Func<int, int> func = x => x;
34+
35+
for (int j = 0; j < iters; j++)
36+
for (int i = 0; i < capacity + 1; i++)
37+
{
38+
dictionary.GetOrAdd(i, func);
39+
40+
// simulate what the LRU does
41+
if (i == capacity)
42+
{
43+
dictionary.TryRemove(1, out var removed);
44+
}
45+
}
46+
}
47+
48+
[Benchmark(OperationsPerInvoke = 24)]
49+
public void FastConcurrentLru()
50+
{
51+
Func<int, int> func = x => x;
52+
53+
for (int j = 0; j < iters; j++)
54+
for (int i = 0; i < capacity + 1; i++)
55+
{
56+
fastConcurrentLru.GetOrAdd(i, func);
57+
}
58+
}
59+
60+
[Benchmark(OperationsPerInvoke = 24)]
61+
public void ConcurrentLru()
62+
{
63+
Func<int, int> func = x => x;
64+
65+
for (int j = 0; j < iters; j++)
66+
for (int i = 0; i < capacity + 1; i++)
67+
{
68+
concurrentLru.GetOrAdd(i, func);
69+
}
70+
}
71+
72+
[Benchmark(OperationsPerInvoke = 24)]
73+
public void FastConcurrentTLru()
74+
{
75+
Func<int, int> func = x => x;
76+
77+
for (int j = 0; j < iters; j++)
78+
for (int i = 0; i < capacity + 1; i++)
79+
{
80+
fastConcurrentTLru.GetOrAdd(i, func);
81+
}
82+
}
83+
84+
[Benchmark(OperationsPerInvoke = 24)]
85+
public void ConcurrentTLru()
86+
{
87+
Func<int, int> func = x => x;
88+
89+
for (int j = 0; j < iters; j++)
90+
for (int i = 0; i < capacity + 1; i++)
91+
{
92+
concurrentTLru.GetOrAdd(i, func);
93+
}
94+
}
95+
96+
[Benchmark(OperationsPerInvoke = 24)]
97+
public void ClassicLru()
98+
{
99+
Func<int, int> func = x => x;
100+
101+
for (int j = 0; j < iters; j++)
102+
for (int i = 0; i < capacity + 1; i++)
103+
{
104+
classicLru.GetOrAdd(i, func);
105+
}
106+
}
107+
108+
[Benchmark(OperationsPerInvoke = 24)]
109+
public void MemoryCache()
110+
{
111+
112+
Func<int, int> func = x => x;
113+
114+
for (int j = 0; j < iters; j++)
115+
for (int i = 0; i < capacity + 1; i++)
116+
{
117+
string key = i.ToString();
118+
var v = memoryCache.Get(key);
119+
120+
if (v == null)
121+
{
122+
memoryCache.Set(key, "test", new CacheItemPolicy());
123+
}
124+
125+
// simulate what the LRU does
126+
if (i == capacity)
127+
{
128+
memoryCache.Remove("1");
129+
}
130+
}
131+
}
132+
}
133+
}

BitFaster.Caching.Benchmarks/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Program
1515
static void Main(string[] args)
1616
{
1717
var summary = BenchmarkRunner
18-
.Run<MissHitHitRemove>(ManualConfig.Create(DefaultConfig.Instance)
18+
.Run<LruCycle2>(ManualConfig.Create(DefaultConfig.Instance)
1919
.AddJob(Job.RyuJitX64));
2020
}
2121
}

BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,31 @@ public class ClassicLruTests
1616
private ClassicLru<int, string> lru = new ClassicLru<int, string>(1, capacity, EqualityComparer<int>.Default);
1717
ValueFactory valueFactory = new ValueFactory();
1818

19-
[Fact]
19+
[Fact]
20+
public void WhenConcurrencyIsLessThan1CtorThrows()
21+
{
22+
Action constructor = () => { var x = new ClassicLru<int, string>(0, 3, EqualityComparer<int>.Default); };
23+
24+
constructor.Should().Throw<ArgumentOutOfRangeException>();
25+
}
26+
27+
[Fact]
28+
public void WhenCapacityIsLessThan3CtorThrows()
29+
{
30+
Action constructor = () => { var x = new ClassicLru<int, string>(1, 2, EqualityComparer<int>.Default); };
31+
32+
constructor.Should().Throw<ArgumentOutOfRangeException>();
33+
}
34+
35+
[Fact]
36+
public void WhenComparerIsNullCtorThrows()
37+
{
38+
Action constructor = () => { var x = new ClassicLru<int, string>(1, 3, null); };
39+
40+
constructor.Should().Throw<ArgumentNullException>();
41+
}
42+
43+
[Fact]
2044
public void WhenItemIsAddedCountIsCorrect()
2145
{
2246
lru.Count.Should().Be(0);
@@ -149,7 +173,37 @@ public void WhenMoreKeysRequestedThanCapacityOldestItemIsEvicted()
149173
valueFactory.timesCalled.Should().Be(capacity + 2);
150174
}
151175

152-
[Fact]
176+
[Fact]
177+
public void WhenValueExpiresItIsDisposed()
178+
{
179+
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
180+
var disposableValueFactory = new DisposableValueFactory();
181+
182+
for (int i = 0; i < 7; i++)
183+
{
184+
lruOfDisposable.GetOrAdd(i, disposableValueFactory.Create);
185+
}
186+
187+
disposableValueFactory.Items[0].IsDisposed.Should().BeTrue();
188+
disposableValueFactory.Items[1].IsDisposed.Should().BeFalse();
189+
}
190+
191+
[Fact]
192+
public async Task WhenValueExpiresAsyncItIsDisposed()
193+
{
194+
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
195+
var disposableValueFactory = new DisposableValueFactory();
196+
197+
for (int i = 0; i < 7; i++)
198+
{
199+
await lruOfDisposable.GetOrAddAsync(i, disposableValueFactory.CreateAsync);
200+
}
201+
202+
disposableValueFactory.Items[0].IsDisposed.Should().BeTrue();
203+
disposableValueFactory.Items[1].IsDisposed.Should().BeFalse();
204+
}
205+
206+
[Fact]
153207
public void WhenKeyDoesNotExistTryGetReturnsFalse()
154208
{
155209
lru.GetOrAdd(1, valueFactory.Create);
@@ -176,7 +230,19 @@ public void WhenKeyExistsTryRemoveRemovesItemAndReturnsTrue()
176230
lru.TryGet(1, out var value).Should().BeFalse();
177231
}
178232

179-
[Fact]
233+
[Fact]
234+
public void WhenItemIsRemovedItIsDisposed()
235+
{
236+
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
237+
var disposableValueFactory = new DisposableValueFactory();
238+
239+
lruOfDisposable.GetOrAdd(1, disposableValueFactory.Create);
240+
lruOfDisposable.TryRemove(1);
241+
242+
disposableValueFactory.Items[1].IsDisposed.Should().BeTrue();
243+
}
244+
245+
[Fact]
180246
public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
181247
{
182248
lru.GetOrAdd(1, valueFactory.Create);

BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -365,29 +365,5 @@ public void WhenRepeatedlyAddingAndRemovingSameValueLruRemainsInConsistentState(
365365
lru.TryRemove(1);
366366
}
367367
}
368-
369-
private class DisposableItem : IDisposable
370-
{
371-
public bool IsDisposed { get; private set; }
372-
373-
public void Dispose()
374-
{
375-
this.IsDisposed = true;
376-
}
377-
}
378-
379-
private class DisposableValueFactory
380-
{
381-
private Dictionary<int, DisposableItem> items = new Dictionary<int, DisposableItem>();
382-
383-
public Dictionary<int, DisposableItem> Items => this.items;
384-
385-
public DisposableItem Create(int key)
386-
{
387-
var item = new DisposableItem();
388-
items.Add(key, item);
389-
return item;
390-
}
391-
}
392368
}
393369
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace BitFaster.Caching.UnitTests.Lru
6+
{
7+
public class DisposableItem : IDisposable
8+
{
9+
public bool IsDisposed { get; private set; }
10+
11+
public void Dispose()
12+
{
13+
this.IsDisposed = true;
14+
}
15+
}
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
6+
namespace BitFaster.Caching.UnitTests.Lru
7+
{
8+
public class DisposableValueFactory
9+
{
10+
private Dictionary<int, DisposableItem> items = new Dictionary<int, DisposableItem>();
11+
12+
public Dictionary<int, DisposableItem> Items => this.items;
13+
14+
public DisposableItem Create(int key)
15+
{
16+
var item = new DisposableItem();
17+
items.Add(key, item);
18+
return item;
19+
}
20+
21+
public Task<DisposableItem> CreateAsync(int key)
22+
{
23+
var item = new DisposableItem();
24+
items.Add(key, item);
25+
return Task.FromResult(item);
26+
}
27+
}
28+
}

BitFaster.Caching.UnitTests/ReferenceCountTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,21 @@ public void WhenObjectsAreDifferentHashcodesAreDifferent()
5252

5353
a.GetHashCode().Should().NotBe(b.GetHashCode());
5454
}
55+
56+
[Fact]
57+
public void WhenObjectDisposed()
58+
{
59+
var a = new ReferenceCount<Disposable>(new Disposable());
60+
var b = a.DecrementCopy();
61+
62+
b.Invoking(rc => rc.IncrementCopy()).Should().Throw<ObjectDisposedException>();
63+
}
64+
65+
private class Disposable : IDisposable
66+
{
67+
public void Dispose()
68+
{
69+
}
70+
}
5571
}
5672
}

0 commit comments

Comments
 (0)