Skip to content

Commit ee1da5f

Browse files
authored
fix atomic factory soak test (#419)
* volatile * defer volatile * detect evicted * dict * use dict ---------
1 parent 42f1f11 commit ee1da5f

File tree

2 files changed

+15
-18
lines changed

2 files changed

+15
-18
lines changed
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Linq;
1+
using System.Collections.Concurrent;
2+
using System.Linq;
23
using System.Threading.Tasks;
4+
using BitFaster.Caching.Atomic;
35
using BitFaster.Caching.Lru;
46
using FluentAssertions;
57
using Xunit;
@@ -12,19 +14,20 @@ public class AtomicFactorySoakTests
1214
[Fact]
1315
public async Task WhenGetOrAddIsConcurrentValuesCreatedAtomically()
1416
{
15-
var cache = new ConcurrentLruBuilder<int, int>().WithAtomicGetOrAdd().WithCapacity(1024).Build();
17+
const int threads = 4;
18+
const int items = 1024;
19+
var dictionary = new ConcurrentDictionary<int, AtomicFactory<int, int>>(concurrencyLevel: threads, capacity: items);
20+
var counters = new int[threads];
1621

17-
var counters = new int[4];
18-
19-
await Threaded.Run(4, (r) =>
22+
await Threaded.Run(threads, (r) =>
2023
{
21-
for (int i = 0; i < 1024; i++)
24+
for (int i = 0; i < items; i++)
2225
{
23-
cache.GetOrAdd(i, k => { counters[r]++; return k; });
26+
dictionary.GetOrAdd(i, k => { counters[r]++; return k; });
2427
}
2528
});
2629

27-
counters.Sum(x => x).Should().Be(1024);
30+
counters.Sum(x => x).Should().Be(items);
2831
}
2932
}
3033
}

BitFaster.Caching/Atomic/AtomicFactory.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public V GetValue<TArg>(K key, Func<K, TArg, V> valueFactory, TArg factoryArgume
7474
/// <summary>
7575
/// Gets a value indicating whether the value has been initialized.
7676
/// </summary>
77-
public bool IsValueCreated => initializer == null;
77+
public bool IsValueCreated => Volatile.Read(ref initializer) == null;
7878

7979
/// <summary>
8080
/// Gets the value if it has been initialized, else default.
@@ -94,12 +94,12 @@ public V ValueIfCreated
9494

9595
private V CreateValue<TFactory>(K key, TFactory valueFactory) where TFactory : struct, IValueFactory<K, V>
9696
{
97-
var init = initializer;
97+
var init = Volatile.Read(ref initializer);
9898

9999
if (init != null)
100100
{
101101
value = init.CreateValue(key, valueFactory);
102-
initializer = null;
102+
Volatile.Write(ref initializer, null); // volatile write must occur after setting value
103103
}
104104

105105
return value;
@@ -135,18 +135,12 @@ public override int GetHashCode()
135135

136136
private class Initializer
137137
{
138-
private readonly object syncLock = new();
139138
private bool isInitialized;
140139
private V value;
141140

142141
public V CreateValue<TFactory>(K key, TFactory valueFactory) where TFactory : struct, IValueFactory<K, V>
143142
{
144-
if (Volatile.Read(ref isInitialized))
145-
{
146-
return value;
147-
}
148-
149-
lock (syncLock)
143+
lock (this)
150144
{
151145
if (Volatile.Read(ref isInitialized))
152146
{

0 commit comments

Comments
 (0)