Skip to content

Commit 86cb7e2

Browse files
author
Alex Peck
committed
soak test
1 parent 8415567 commit 86cb7e2

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using BitFaster.Caching.Lru;
5+
using Xunit;
6+
7+
namespace BitFaster.Caching.UnitTests.Lru
8+
{
9+
[Collection("Soak")]
10+
public class LruItemSoakTests
11+
{
12+
private const int soakIterations = 3;
13+
private readonly LruItem<int, MassiveStruct> item = new(1, MassiveStruct.A);
14+
15+
// Adapted from
16+
// https://stackoverflow.com/questions/23262513/reproduce-torn-reads-of-decimal-in-c-sharp
17+
[Theory]
18+
[Repeat(soakIterations)]
19+
#pragma warning disable xUnit1026
20+
public async Task DetectTornStruct(int iteration)
21+
#pragma warning restore xUnit1026
22+
{
23+
using var source = new CancellationTokenSource();
24+
var started = new TaskCompletionSource<bool>();
25+
26+
var setTask = Task.Run(() => Setter(source.Token, started));
27+
await started.Task;
28+
Checker(source);
29+
30+
await setTask;
31+
}
32+
33+
void Setter(CancellationToken cancelToken, TaskCompletionSource<bool> started)
34+
{
35+
started.SetResult(true);
36+
37+
while (true)
38+
{
39+
item.SeqLockWrite(MassiveStruct.A);
40+
item.SeqLockWrite(MassiveStruct.B);
41+
42+
if (cancelToken.IsCancellationRequested)
43+
{
44+
return;
45+
}
46+
}
47+
}
48+
49+
void Checker(CancellationTokenSource source)
50+
{
51+
// On my machine, without SeqLock, this consistently fails below 100 iterations
52+
// on debug build, and below 1000 on release build
53+
for (int count = 0; count < 10_000; ++count)
54+
{
55+
var t = item.SeqLockRead();
56+
57+
if (t != MassiveStruct.A && t != MassiveStruct.B)
58+
{
59+
throw new Exception($"Value is torn after {count} iterations");
60+
}
61+
}
62+
63+
source.Cancel();
64+
}
65+
66+
#pragma warning disable CS0659 // Object.Equals but no GetHashCode
67+
#pragma warning disable CS0661 // operator== but no GetHashCode
68+
public struct MassiveStruct : IEquatable<MassiveStruct>
69+
{
70+
// To repro on x64, struct should be larger than a cache line (64 bytes).
71+
public long a;
72+
public long b;
73+
public long c;
74+
public long d;
75+
76+
public long e;
77+
public long f;
78+
public long g;
79+
public long h;
80+
81+
public long i;
82+
83+
public static readonly MassiveStruct A = new MassiveStruct();
84+
public static readonly MassiveStruct B = new MassiveStruct()
85+
{ a = long.MaxValue, b = long.MaxValue, c = long.MaxValue, d = long.MaxValue, e = long.MaxValue, f= long.MaxValue, g = long.MaxValue, h = long.MaxValue, i = long.MaxValue };
86+
87+
public override bool Equals(object obj)
88+
{
89+
return obj is MassiveStruct @struct && Equals(@struct);
90+
}
91+
92+
public bool Equals(MassiveStruct other)
93+
{
94+
return a == other.a &&
95+
b == other.b &&
96+
c == other.c &&
97+
d == other.d &&
98+
e == other.e &&
99+
f == other.f &&
100+
g == other.g &&
101+
h == other.h &&
102+
i == other.i;
103+
}
104+
105+
public static bool operator ==(MassiveStruct left, MassiveStruct right)
106+
{
107+
return left.Equals(right);
108+
}
109+
110+
public static bool operator !=(MassiveStruct left, MassiveStruct right)
111+
{
112+
return !(left == right);
113+
}
114+
}
115+
#pragma warning restore CS0659
116+
#pragma warning restore CS0661
117+
}
118+
}

0 commit comments

Comments
 (0)