Skip to content

Commit 87fcd06

Browse files
author
Alex Peck
committed
seqlock
1 parent be8f0ed commit 87fcd06

File tree

3 files changed

+50
-5
lines changed

3 files changed

+50
-5
lines changed

BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAddGuid.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System;
1010
using System.Collections.Concurrent;
1111
using System.Collections.Generic;
12+
using System.Threading;
1213

1314
namespace BitFaster.Caching.Benchmarks
1415
{

BitFaster.Caching/Lru/ConcurrentLruCore.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,7 @@ private bool GetOrDiscard(I item, [MaybeNullWhen(false)] out V value)
186186
else
187187
{
188188
// prevent torn read for non-atomic types
189-
lock (item)
190-
{
191-
value = item.Value;
192-
}
189+
value = item.SeqLockRead();
193190
}
194191

195192
this.itemPolicy.Touch(item);
@@ -394,7 +391,16 @@ public bool TryUpdate(K key, V value)
394391
if (!existing.WasRemoved)
395392
{
396393
V oldValue = existing.Value;
397-
existing.Value = value;
394+
395+
if (TypeProps<V>.IsWriteAtomic)
396+
{
397+
existing.Value = value;
398+
}
399+
else
400+
{
401+
existing.SeqLockWrite(value);
402+
}
403+
398404
this.itemPolicy.Update(existing);
399405
// backcompat: remove conditional compile
400406
#if NETCOREAPP3_0_OR_GREATER

BitFaster.Caching/Lru/LruItem.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
using System.Threading;
3+
24
namespace BitFaster.Caching.Lru
35
{
46
/// <summary>
@@ -11,6 +13,8 @@ public class LruItem<K, V>
1113
private volatile bool wasAccessed;
1214
private volatile bool wasRemoved;
1315

16+
private int sequence;
17+
1418
/// <summary>
1519
/// Initializes a new instance of the LruItem class with the specified key and value.
1620
/// </summary>
@@ -49,5 +53,39 @@ public bool WasRemoved
4953
get => this.wasRemoved;
5054
set => this.wasRemoved = value;
5155
}
56+
57+
internal V SeqLockRead()
58+
{
59+
var spin = new SpinWait();
60+
while (true)
61+
{
62+
var start = Volatile.Read(ref this.sequence);
63+
64+
if ((start & 1) == 1)
65+
{
66+
// A write is in progress. Back off and keep spinning.
67+
spin.SpinOnce();
68+
continue;
69+
}
70+
71+
V copy = this.Value;
72+
73+
var end = Volatile.Read(ref this.sequence);
74+
if (start == end)
75+
{
76+
return copy;
77+
}
78+
}
79+
}
80+
81+
// Note: item should be locked while invoking this method. Multiple writer threads are not supported.
82+
internal void SeqLockWrite(V value)
83+
{
84+
Interlocked.Increment(ref sequence);
85+
86+
this.Value = value;
87+
88+
Interlocked.Increment(ref sequence);
89+
}
5290
}
5391
}

0 commit comments

Comments
 (0)