Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 25 additions & 89 deletions RiotSharp/Caching/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,17 @@ namespace RiotSharp.Caching
/// </summary>
public class Cache : ICache
{
private readonly IDictionary<object, CacheItem> _cache = new Dictionary<object, CacheItem>();
private readonly IDictionary<object, SlidingDetails> _slidingTimes = new Dictionary<object, SlidingDetails>();
private readonly IDictionary<object, CacheData<object>> _cache = new Dictionary<object, CacheData<object>>();

private const int DefaultMonitorWait = 1000;
private const int MonitorWaitToUpdateSliding = 500;

private readonly object _sync = new object();

#region ICache interface
/// <inheritdoc />
public void Add<TK, TV>(TK key, TV value, TimeSpan slidingExpiry) where TV : class
{
Add(key, value, slidingExpiry, true);
Store(key, value, slidingExpiry);
}

/// <inheritdoc />
Expand All @@ -31,45 +29,22 @@ public void Add<TK, TV>(TK key, TV value, DateTime absoluteExpiry) where TV : cl
if (absoluteExpiry > DateTime.Now)
{
var diff = absoluteExpiry - DateTime.Now;
Add(key, value, diff, false);
Store(key, value, diff);
}
}

/// <inheritdoc />
public TV Get<TK, TV>(TK key) where TV : class
{
if (_cache.ContainsKey(key))
{
var cacheItem = _cache[key];

if (cacheItem.RelativeExpiry.HasValue)
{
if (Monitor.TryEnter(_sync, MonitorWaitToUpdateSliding))
{
try
{
_slidingTimes[key].Viewed();
}
finally
{
Monitor.Exit(_sync);
}
}
}

return (TV)cacheItem.Value;
}

return null;
return Load<TK, TV>(key);
}

/// <inheritdoc />
public void Remove<TK>(TK key)
{
if (!Equals(key, null))
{
_cache.Remove(key);
_slidingTimes.Remove(key);
}
}

Expand All @@ -81,7 +56,6 @@ public void Clear()
try
{
_cache.Clear();
_slidingTimes.Clear();
}
finally
{
Expand Down Expand Up @@ -146,7 +120,7 @@ internal IEnumerable<TV> Values<TV>() where TV : class
try
{
return _cache.Values
.Select(cacheItem => cacheItem.Value)
.Select(cacheItem => cacheItem.Data)
.Where(v => v.GetType() == typeof(TV))
.Cast<TV>().ToList();
}
Expand All @@ -169,7 +143,7 @@ internal IEnumerable<object> Values()
{
try
{
return _cache.Values.Select(cacheItem => cacheItem.Value).ToList();
return _cache.Values.Select(cacheItem => cacheItem.Data).ToList();
}
finally
{
Expand Down Expand Up @@ -201,86 +175,48 @@ internal int Count()
return -1;
}

private void Add<TK, TV>(TK key, TV value, TimeSpan timeSpan, bool isSliding) where TV : class
private TV Load<TK, TV>(TK key) where TV : class
{
CacheData<TV> data = null;
if (Monitor.TryEnter(_sync, DefaultMonitorWait))
{
try
{
Remove(key);
_cache.Add(key, new CacheItem(value, isSliding ? timeSpan : (TimeSpan?)null));

if (isSliding)
CacheData<object> objData;
this._cache.TryGetValue(key, out objData);
if (objData.Data is TV)
{
_slidingTimes.Add(key, new SlidingDetails(timeSpan));
data = objData as CacheData<TV>;
}

StartObserving(key, timeSpan);
}
finally
{
Monitor.Exit(_sync);
}
}
}

private void StartObserving<TK>(TK key, TimeSpan timeSpan)
{
Timer timer = null;
timer = new Timer(x =>
{
TryPurgeItem(key);
timer?.Dispose();
}, key, timeSpan, TimeSpan.FromMilliseconds(-1));
return IsExpired(data) ? null : data.Data;
}

private void TryPurgeItem<TK>(TK key)
private void Store<TK, TV>(TK key, TV value, TimeSpan timeSpan) where TV : class
{
if (_slidingTimes.ContainsKey(key))
if (Monitor.TryEnter(_sync, DefaultMonitorWait))
{
if (!_slidingTimes[key].CanExpire(out var tryAfter))
try
{
StartObserving(key, tryAfter);
return;
Remove(key);
_cache.Add(key, new CacheData<object>((long)timeSpan.TotalMinutes, value));
}
finally
{
Monitor.Exit(_sync);
}
}

Remove(key);
}

private class CacheItem
{
public CacheItem(object value, TimeSpan? relativeExpiry)
{
Value = value;
RelativeExpiry = relativeExpiry;
}

public object Value { get; }
public TimeSpan? RelativeExpiry { get; }
}

private class SlidingDetails
private bool IsExpired<T>(CacheData<T> data)
{
private readonly TimeSpan _relativeExpiry;
private DateTime _expireAt;

public SlidingDetails(TimeSpan relativeExpiry)
{
_relativeExpiry = relativeExpiry;
Viewed();
}

public bool CanExpire(out TimeSpan tryAfter)
{
tryAfter = _expireAt - DateTime.Now;
return (0 > tryAfter.Ticks);
}

public void Viewed()
{
_expireAt = DateTime.Now.Add(_relativeExpiry);
}
return data == null || DateTime.Now > data.CreatedAt.AddMinutes(data.TtlMinutes);
}
}
}