@ -1,8 +1,10 @@
using System ;
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading.Tasks ;
using NzbDrone.Common.EnsureThat ;
using NzbDrone.Common.Extensions ;
namespace NzbDrone.Common.Cache
{
@ -29,40 +31,56 @@ namespace NzbDrone.Common.Cache
}
private readonly ConcurrentDictionary < string , CacheItem > _store ;
private readonly TimeSpan ? _defaultLifeTime ;
private readonly bool _rollingExpiry ;
public Cached ( )
public Cached ( TimeSpan ? defaultLifeTime = null , bool rollingExpiry = false )
{
_store = new ConcurrentDictionary < string , CacheItem > ( ) ;
_defaultLifeTime = defaultLifeTime ;
_rollingExpiry = rollingExpiry ;
}
public void Set ( string key , T value , TimeSpan ? life t ime = null )
public void Set ( string key , T value , TimeSpan ? life T ime = null )
{
Ensure . That ( key , ( ) = > key ) . IsNotNullOrWhiteSpace ( ) ;
_store [ key ] = new CacheItem ( value , lifetime ) ;
if ( lifetime ! = null )
_store [ key ] = new CacheItem ( value , lifeTime ? ? _defaultLifeTime ) ;
if ( lifeTime ! = null )
{
System . Threading . Tasks . Task . Delay ( lifetime . Value ) . ContinueWith ( t = > _store . TryRemove ( key , out var temp ) ) ;
S cheduleTryRemove( key , lifeTime . Value ) ;
}
}
public T Find ( string key )
{
CacheItem value ;
_store . TryGetValue ( key , out value ) ;
if ( value = = null )
CacheItem cacheItem ;
if ( ! _store . TryGetValue ( key , out cacheItem ) )
{
return default ( T ) ;
}
if ( value . IsExpired ( ) )
if ( cacheItem . IsExpired ( ) )
{
_store . TryRemove ( key , out value ) ;
return default ( T ) ;
if ( TryRemove ( key , cacheItem ) )
{
return default ( T ) ;
}
if ( ! _store . TryGetValue ( key , out cacheItem ) )
{
return default ( T ) ;
}
}
if ( _rollingExpiry & & _defaultLifeTime . HasValue )
{
_store . TryUpdate ( key , new CacheItem ( cacheItem . Object , _defaultLifeTime . Value ) , cacheItem ) ;
ScheduleTryRemove ( key , _defaultLifeTime . Value ) ;
}
return value . Object ;
return cacheItem . Object ;
}
public void Remove ( string key )
@ -77,20 +95,32 @@ namespace NzbDrone.Common.Cache
{
Ensure . That ( key , ( ) = > key ) . IsNotNullOrWhiteSpace ( ) ;
lifeTime = lifeTime ? ? _defaultLifeTime ;
CacheItem cacheItem ;
T value ;
if ( ! _store . TryGetValue ( key , out cacheItem ) || cacheItem . IsExpired ( ) )
if ( _store . TryGetValue ( key , out cacheItem ) && ! cacheItem . IsExpired ( ) )
{
value = function ( ) ;
Set ( key , value , lifeTime ) ;
if ( _rollingExpiry & & lifeTime . HasValue )
{
_store . TryUpdate ( key , new CacheItem ( cacheItem . Object , lifeTime ) , cacheItem ) ;
ScheduleTryRemove ( key , lifeTime . Value ) ;
}
}
else
{
value = cacheItem . Object ;
var newCacheItem = new CacheItem ( function ( ) , lifeTime ) ;
if ( cacheItem ! = null & & _store . TryUpdate ( key , newCacheItem , cacheItem ) )
{
cacheItem = newCacheItem ;
}
else
{
cacheItem = _store . GetOrAdd ( key , newCacheItem ) ;
}
}
return value ;
return cacheItem . Object ;
}
public void Clear ( )
@ -100,9 +130,11 @@ namespace NzbDrone.Common.Cache
public void ClearExpired ( )
{
foreach ( var cached in _store . Where ( c = > c . Value . IsExpired ( ) ) )
var collection = ( ICollection < KeyValuePair < string , CacheItem > > ) _store ;
foreach ( var cached in _store . Where ( c = > c . Value . IsExpired ( ) ) . ToList ( ) )
{
Remove ( cached . Key ) ;
collection. Remove( cached ) ;
}
}
@ -113,5 +145,23 @@ namespace NzbDrone.Common.Cache
return _store . Values . Select ( c = > c . Object ) . ToList ( ) ;
}
}
private bool TryRemove ( string key , CacheItem value )
{
var collection = ( ICollection < KeyValuePair < string , CacheItem > > ) _store ;
return collection . Remove ( new KeyValuePair < string , CacheItem > ( key , value ) ) ;
}
private void ScheduleTryRemove ( string key , TimeSpan lifeTime )
{
Task . Delay ( lifeTime ) . ContinueWith ( t = >
{
if ( _store . TryGetValue ( key , out var cacheItem ) & & cacheItem . IsExpired ( ) )
{
_store . TryRemove ( key , out _ ) ;
}
} ) ;
}
}
}