/// <summary> /// Prune cache. /// </summary> void Prune(object _discard, System.Timers.ElapsedEventArgs _2) { if (disposed) { return; // dumbness with timers } if (!Atomic.Lock(ref prune_in_progress)) { return; // only one instance. } try { if (Items.Count <= MinCache) { return; // just don't bother } if (Items.Count <= AbsoluteCapacity && CacheEvictStrategy == EvictStrategy.LeastUsed) { return; } var list = Items.Values.ToList(); // would be nice to cache this list list.Sort((CacheItem <TKey, TValue> x, CacheItem <TKey, TValue> y) => { if (CacheEvictStrategy == EvictStrategy.LeastRecent) { if (x.Access < y.Access) { return(-1); } if (x.Access > y.Access) { return(1); } // Both have equal at this point if (x.Desirability < y.Desirability) { return(-1); } if (x.Desirability > y.Desirability) { return(1); } } else // EvictStrategy.LeastUsed { if (x.Desirability < y.Desirability) { return(-1); } if (x.Desirability > y.Desirability) { return(1); } // Both have equal at this point if (x.Access < y.Access) { return(-1); } if (x.Access > y.Access) { return(1); } } return(0); }); while (Items.Count > AbsoluteCapacity) { var bu = list[0]; var key = bu.AccessKey; Items.TryRemove(key, out _); list.Remove(bu); } var deleteItem = new Action <TKey>( (TKey key) => { //Debug.WriteLine($"MKAh.SimpleCache removing {bi:N1} min old item."); if (Items.TryRemove(key, out var item)) { list.Remove(item); } } ); var now = DateTimeOffset.UtcNow; while (list.Count > 0) { var bu = list[0]; double bi = now.Since(bu.Access).TotalMinutes; if (CacheEvictStrategy == EvictStrategy.LeastRecent) { if (bi > MaxAge) { deleteItem(bu.AccessKey); } else { break; } } else // .LeastUsed { if (bi > MinAge) { deleteItem(bu.AccessKey); } else { break; } } } } finally { Atomic.Unlock(ref prune_in_progress); } }