22using System . Collections ;
33using System . Collections . Concurrent ;
44using System . Collections . Generic ;
5+ using System . Runtime . CompilerServices ;
56using System . Threading ;
67using System . Threading . Tasks ;
78
@@ -230,24 +231,17 @@ public bool TryAdd(TKey key, TValue value, TimeSpan ttl)
230231 return _dict . TryUpdate ( key , new TtlValue ( value , ttl ) , existing ) ;
231232 }
232233
233- private TValue GetOrAddCore ( TKey key , Func < TValue > valueFactory , TimeSpan ttl )
234+ private TValue GetOrAddCore < TArg > ( TKey key , Func < TKey , TArg , TValue > valueFactory , TArg factoryArg , long ttlMs )
234235 {
235- bool wasAdded = false ; //flag to indicate "add vs get". TODO: wrap in ref type some day to avoid captures/closures
236236 var ttlValue = _dict . GetOrAdd (
237237 key ,
238- ( _ ) =>
239- {
240- wasAdded = true ;
241- return new TtlValue ( valueFactory ( ) , ttl ) ;
242- } ) ;
238+ static ( k , arg ) => new TtlValue ( arg . valueFactory ( k , arg . factoryArg ) , arg . ttlMs ) ,
239+ ( valueFactory , factoryArg , ttlMs ) ) ;
243240
244241 //if the item is expired, update value and TTL
245242 //since TtlValue is a reference type we can update its properties in-place, instead of removing and re-adding to the dictionary (extra lookups)
246- if ( ! wasAdded ) //performance hack: skip expiration check if a brand item was just added
247- {
248- if ( ttlValue . ModifyIfExpired ( valueFactory , ttl ) )
249- OnEviction ( key ) ;
250- }
243+ if ( ttlValue . ModifyIfExpired ( static args => args . valueFactory ( args . key , args . factoryArg ) , ( valueFactory , key , factoryArg ) , ttlMs ) )
244+ OnEviction ( key ) ;
251245
252246 return ttlValue . Value ;
253247 }
@@ -259,7 +253,7 @@ private TValue GetOrAddCore(TKey key, Func<TValue> valueFactory, TimeSpan ttl)
259253 /// <param name="valueFactory">The factory function used to generate the item for the key</param>
260254 /// <param name="ttl">TTL of the item</param>
261255 public TValue GetOrAdd ( TKey key , Func < TKey , TValue > valueFactory , TimeSpan ttl )
262- => GetOrAddCore ( key , ( ) => valueFactory ( key ) , ttl ) ;
256+ => GetOrAddCore ( key , static ( k , f ) => f ( k ) , valueFactory , ( long ) ttl . TotalMilliseconds ) ;
263257
264258 /// <summary>
265259 /// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
@@ -269,7 +263,7 @@ public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory, TimeSpan ttl)
269263 /// <param name="ttl">TTL of the item</param>
270264 /// <param name="factoryArgument">Argument value to pass into valueFactory</param>
271265 public TValue GetOrAdd < TArg > ( TKey key , Func < TKey , TArg , TValue > valueFactory , TimeSpan ttl , TArg factoryArgument )
272- => GetOrAddCore ( key , ( ) => valueFactory ( key , factoryArgument ) , ttl ) ;
266+ => GetOrAddCore ( key , static ( k , args ) => args . valueFactory ( k , args . factoryArgument ) , ( valueFactory , factoryArgument ) , ( long ) ttl . TotalMilliseconds ) ;
273267
274268 /// <summary>
275269 /// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
@@ -278,7 +272,7 @@ public TValue GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, Ti
278272 /// <param name="value">The value to add</param>
279273 /// <param name="ttl">TTL of the item</param>
280274 public TValue GetOrAdd ( TKey key , TValue value , TimeSpan ttl )
281- => GetOrAddCore ( key , ( ) => value , ttl ) ;
275+ => GetOrAddCore ( key , static ( _ , v ) => v , value , ( long ) ttl . TotalMilliseconds ) ;
282276
283277 /// <summary>
284278 /// Tries to remove item with the specified key
@@ -355,6 +349,12 @@ private class TtlValue
355349 public TValue Value { get ; private set ; }
356350 private long TickCountWhenToKill ;
357351
352+ public TtlValue ( TValue value , long ttlMs )
353+ {
354+ Value = value ;
355+ TickCountWhenToKill = Environment . TickCount64 + ttlMs ;
356+ }
357+
358358 public TtlValue ( TValue value , TimeSpan ttl )
359359 {
360360 Value = value ;
@@ -369,16 +369,16 @@ public TtlValue(TValue value, TimeSpan ttl)
369369 public bool IsExpired ( long currTime ) => currTime > TickCountWhenToKill ;
370370
371371 /// <summary>
372- /// Updates the value and TTL only if the item is expired
372+ /// Updates the value and TTL only if the item is expired, using a factory function
373373 /// </summary>
374374 /// <returns>True if the item expired and was updated, otherwise false</returns>
375- public bool ModifyIfExpired ( Func < TValue > newValueFactory , TimeSpan newTtl )
375+ public bool ModifyIfExpired < TArg > ( Func < TArg , TValue > newValueFactory , TArg arg , long ttlMs )
376376 {
377377 var ticks = Environment . TickCount64 ; //save to a var to prevent multiple calls to Environment.TickCount64
378378 if ( IsExpired ( ticks ) ) //if expired - update the value and TTL
379379 {
380- TickCountWhenToKill = ticks + ( long ) newTtl . TotalMilliseconds ; //update the expiration time first for better concurrency
381- Value = newValueFactory ( ) ;
380+ TickCountWhenToKill = ticks + ttlMs ; //update the expiration time first for better concurrency
381+ Value = newValueFactory ( arg ) ;
382382 return true ;
383383 }
384384 return false ;
0 commit comments