@@ -14,13 +14,12 @@ private struct Entry { internal int Bucket; internal int Next; internal K Key; i
1414 private sealed class Pending { internal required K Key ; internal required Task < V > Value ; internal Pending ? Next ; }
1515 private readonly Lock _lock = new ( ) ;
1616 private int _count ;
17- private int _version ;
1817 private Entry [ ] _entries ;
1918 private Pending ? _pending ;
2019
2120 public Cache ( int capacity = 2 ) => _entries = new Entry [ capacity <= 2 ? 2 : BitOperations . RoundUpToPowerOf2 ( ( uint ) capacity ) ] ;
2221
23- public Cache ( IEnumerable < KeyValuePair < K , V > > items ) : this ( )
22+ public Cache ( IEnumerable < KeyValuePair < K , V > > items , int capacity = 2 ) : this ( capacity )
2423 {
2524 foreach ( var ( k , v ) in items ) AddOrUpdate ( k , v ) ;
2625 }
@@ -44,10 +43,7 @@ private void AddOrUpdate(K key, V value)
4443 entries [ i ] . Next = entries [ bucketIndex ] . Bucket - 1 ;
4544 entries [ i ] . Key = key ;
4645 entries [ i ] . Value = value ;
47- entries [ bucketIndex ] . Bucket = _count + 1 ;
48- _entries = entries ;
49- _count ++ ;
50- _version ++ ;
46+ _count = entries [ bucketIndex ] . Bucket = _count + 1 ;
5147 }
5248
5349 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
@@ -63,16 +59,15 @@ private Entry[] Resize()
6359 newEntries [ i ] . Value = oldEntries [ i ] . Value ;
6460 newEntries [ bucketIndex ] . Bucket = ++ i ;
6561 }
66- return newEntries ;
62+ return _entries = newEntries ;
6763 }
6864
69- public ValueTask < V > GetOrAdd ( K key , Func < K , Task < V > > factory )
70- => GetOrAdd ( key , factory , static ( k , f ) => f ( k ) ) ;
65+ public ValueTask < V > GetOrAdd ( K key , Func < K , Task < V > > factory ) => GetOrAdd ( key , factory , static ( k , f ) => f ( k ) ) ;
7166
7267 public ValueTask < V > GetOrAdd < TState > ( K key , TState state , Func < K , TState , Task < V > > factory )
7368 {
7469 var hashCode = key . GetHashCode ( ) ;
75- var version = Volatile . Read ( ref _version ) ;
70+ var count = _count ;
7671 var entries = Volatile . Read ( ref _entries ) ;
7772 var i = entries [ hashCode & ( entries . Length - 1 ) ] . Bucket - 1 ;
7873 while ( i >= 0 )
@@ -82,7 +77,7 @@ public ValueTask<V> GetOrAdd<TState>(K key, TState state, Func<K, TState, Task<V
8277 }
8378 lock ( _lock )
8479 {
85- if ( _version != version )
80+ if ( _count != count )
8681 {
8782 entries = _entries ;
8883 i = entries [ hashCode & ( entries . Length - 1 ) ] . Bucket - 1 ;
@@ -96,39 +91,30 @@ public ValueTask<V> GetOrAdd<TState>(K key, TState state, Func<K, TState, Task<V
9691 }
9792 }
9893
99- public Task < V > Update ( K key , Func < K , Task < V > > factory )
100- => Update ( key , factory , static ( k , f ) => f ( k ) ) ;
94+ public Task < V > Update ( K key , Func < K , Task < V > > factory ) => Update ( key , factory , static ( k , f ) => f ( k ) ) ;
10195
10296 public Task < V > Update < TState > ( K key , TState state , Func < K , TState , Task < V > > factory )
10397 {
10498 lock ( _lock )
10599 return GetOrAddPending ( key , state , factory ) . Value ;
106100 }
107101
108- public void Compact ( Func < KeyValuePair < K , V > , bool > keep )
102+ public async Task < Cache < K , V > > Compact ( Func < KeyValuePair < K , V > , bool > keep )
109103 {
110- lock ( _lock )
104+ while ( true )
111105 {
112- var oldCount = _count ;
113- var newCount = this . Count ( keep ) ;
114- if ( newCount == oldCount ) return ;
115- var newEntries = new Entry [ newCount < 2 ? 2 : BitOperations . RoundUpToPowerOf2 ( ( uint ) newCount ) ] ;
116- var oldEntries = _entries ;
117- int newIndex = 0 ;
118- for ( int i = 0 ; i < oldCount ; i ++ )
106+ var pending = _pending ;
107+ while ( pending is not null )
119108 {
120- if ( keep ( new ( oldEntries [ i ] . Key , oldEntries [ i ] . Value ) ) )
121- {
122- var bucketIndex = oldEntries [ i ] . Key . GetHashCode ( ) & ( newEntries . Length - 1 ) ;
123- newEntries [ newIndex ] . Next = newEntries [ bucketIndex ] . Bucket - 1 ;
124- newEntries [ newIndex ] . Key = oldEntries [ i ] . Key ;
125- newEntries [ newIndex ] . Value = oldEntries [ i ] . Value ;
126- newEntries [ bucketIndex ] . Bucket = ++ newIndex ;
127- }
109+ try { await pending . Value ; }
110+ catch { }
111+ pending = pending . Next ;
128112 }
129- _count = newIndex ;
130- _entries = newEntries ;
131- _version ++ ;
113+ var newCount = this . Count ( keep ) ;
114+ var count = _count ;
115+ if ( count == newCount ) return this ;
116+ var newCache = new Cache < K , V > ( this . Where ( keep ) , newCount ) ;
117+ if ( _count == count && _pending is null ) return newCache ;
132118 }
133119 }
134120
@@ -190,17 +176,8 @@ private void RemovePending(Pending remove)
190176
191177 public IEnumerator < KeyValuePair < K , V > > GetEnumerator ( )
192178 {
193- var version = Volatile . Read ( ref _version ) ;
194- var count = _count ;
195- var entries = _entries ;
196- if ( version != Volatile . Read ( ref _version ) )
197- lock ( _lock )
198- {
199- count = _count ;
200- entries = _entries ;
201- }
202- for ( int i = 0 ; i < count ; i ++ )
203- yield return new ( entries [ i ] . Key , entries [ i ] . Value ) ;
179+ for ( int i = 0 ; i < _count ; i ++ )
180+ yield return new ( _entries [ i ] . Key , _entries [ i ] . Value ) ;
204181 }
205182
206183 IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
@@ -230,7 +207,7 @@ public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
230207/// </para></summary>
231208public sealed class RefreshingCache < K , V > : IDisposable where K : IEquatable < K >
232209{
233- private readonly Cache < K , ( long Timestamp , V Value ) > _cache = new ( ) ;
210+ private Cache < K , ( long Timestamp , V Value ) > _cache = new ( ) ;
234211 private readonly long _durationTicks , _eagerRefreshTicks ;
235212 private readonly TimeSpan _softTimeout ;
236213 private readonly Timer ? _timer ;
@@ -245,8 +222,11 @@ public RefreshingCache(TimeSpan duration, double eagerRefreshRatio = 0.5, TimeSp
245222
246223 private void Cleanup ( object ? removeTicks )
247224 {
248- var removeTimestamp = Stopwatch . GetTimestamp ( ) - ( long ) removeTicks ! ;
249- _cache . Compact ( e => e . Value . Timestamp >= removeTimestamp ) ;
225+ _ = Task . Run ( async ( ) =>
226+ {
227+ var removeTimestamp = Stopwatch . GetTimestamp ( ) - ( long ) removeTicks ! ;
228+ _cache = await _cache . Compact ( e => e . Value . Timestamp >= removeTimestamp ) ;
229+ } ) ;
250230 }
251231
252232 private static async Task < ( long , V ) > CallFactory ( K key , Func < K , Task < V > > factory )
0 commit comments