Skip to content

Commit 8dc9e99

Browse files
author
Alex Peck
committed
handle TLFU
1 parent 3e6e9bf commit 8dc9e99

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

BitFaster.Caching/Lfu/ConcurrentLfuCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ public bool TryUpdate(K key, V value)
371371
}
372372
else
373373
{
374-
var newNode = policy.Create(key, value);
374+
var newNode = policy.Clone(node, value);
375375
if (this.dictionary.TryUpdate(key, newNode, node))
376376
{
377377
// This can't be lossy, because we need to attach the new node.

BitFaster.Caching/Lfu/LfuNode.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ public void SetPreviousInTimeOrder(TimeOrderNode<K, V> prev)
114114
this.prevTime = prev;
115115
}
116116

117+
// Note: there is a special case for non-atomic value types. When the node has been updated,
118+
// GetNextInTimeOrder() will return this. In this situation, the node is not attached to the
119+
// timer wheel.
117120
public TimeOrderNode<K, V> GetNextInTimeOrder()
118121
{
119122
return nextTime;

BitFaster.Caching/Lfu/NodePolicy.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal interface INodePolicy<K, V, N>
99
where N : LfuNode<K, V>
1010
{
1111
N Create(K key, V value);
12+
N Clone(N previous, V value);
1213
bool IsExpired(N node);
1314
void AdvanceTime();
1415
void OnRead(N node);
@@ -26,6 +27,11 @@ public AccessOrderNode<K, V> Create(K key, V value)
2627
return new AccessOrderNode<K, V>(key, value);
2728
}
2829

30+
public AccessOrderNode<K, V> Clone(AccessOrderNode<K, V> prev, V value)
31+
{
32+
return new AccessOrderNode<K, V>(prev.Key, value);
33+
}
34+
2935
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3036
public bool IsExpired(AccessOrderNode<K, V> node)
3137
{
@@ -82,6 +88,14 @@ public TimeOrderNode<K, V> Create(K key, V value)
8288
return new TimeOrderNode<K, V>(key, value) { TimeToExpire = Duration.SinceEpoch() + expiry };
8389
}
8490

91+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
92+
public TimeOrderNode<K, V> Clone(TimeOrderNode<K, V> prev, V value)
93+
{
94+
var node = new TimeOrderNode<K, V>(prev.Key, value) { TimeToExpire = prev.TimeToExpire };
95+
node.SetNextInTimeOrder(node);
96+
return node;
97+
}
98+
8599
[MethodImpl(MethodImplOptions.AggressiveInlining)]
86100
public bool IsExpired(TimeOrderNode<K, V> node)
87101
{
@@ -118,9 +132,19 @@ public void OnWrite(TimeOrderNode<K, V> node)
118132
{
119133
var oldTte = node.TimeToExpire;
120134
node.TimeToExpire = current + expiryCalculator.GetExpireAfterUpdate(node.Key, node.Value, node.TimeToExpire - current);
121-
if (oldTte.raw != node.TimeToExpire.raw)
122-
{
123-
wheel.Reschedule(node);
135+
136+
// non-atomic write value node was updated and replaced, schedule
137+
// can JIT elide this branch for atomic writes?
138+
if (!TypeProps<V>.IsWriteAtomic && node.GetNextInTimeOrder() == node)
139+
{
140+
wheel.Schedule(node);
141+
}
142+
else
143+
{
144+
if (oldTte.raw != node.TimeToExpire.raw)
145+
{
146+
wheel.Reschedule(node);
147+
}
124148
}
125149
}
126150
}

0 commit comments

Comments
 (0)