/// <summary> /// A least recently used cache with a time to live. /// </summary> /// <param name="capacity"> /// The number of entries the cache will hold /// </param> /// <param name="hours">The number of hours in the TTL</param> /// <param name="minutes">The number of minutes in the TTL</param> /// <param name="seconds">The number of seconds in the TTL</param> /// <param name="refreshEntries"> /// Whether the TTL should be refreshed upon retrieval /// </param> public LRUCache( int capacity, int hours = 0, int minutes = 0, int seconds = 0, bool refreshEntries = true) { this._capacity = capacity; this._entries = new Dictionary <K, CacheNode>(this._capacity); this._head = null; this._tail = null; this._count = 0; this._ttl = new TimeSpan(hours, minutes, seconds); this._refreshEntries = refreshEntries; if (this._ttl > TimeSpan.Zero) { this._timer = new Timer( Purge, null, (int)this._ttl.TotalMilliseconds, 5000); // 5 seconds } }
private void RemoveFromLL(CacheNode entry) { var next = entry.Next; var prev = entry.Prev; if (null != next) { next.Prev = entry.Prev; } if (null != prev) { prev.Next = entry.Next; } if (this._head == entry) { this._head = next; } if (this._tail == entry) { this._tail = prev; } }
private void RemoveLeastRecentlyUsed() { cachedItems.Remove(tail.Key); tail.Previous.Next = null; tail = tail.Previous; }
/// <summary> /// Sets the item being stored to the supplied value. /// </summary> /// <param name="key">The cache key.</param> /// <param name="value">The value to set in the cache.</param> /// <returns>True if the set was successful. False otherwise.</returns> public bool TryAdd(K key, V value) { CacheNode entry; if (!this._entries.TryGetValue(key, out entry)) { // Add the entry lock (this) { if (!this._entries.TryGetValue(key, out entry)) { if (this.IsFull) { // Re-use the CacheNode entry entry = this._tail; _entries.Remove(this._tail.Key); // Reset with new values entry.Key = key; entry.Value = value; entry.LastAccessed = DateTime.UtcNow; // Next and Prev don't need to be reset. // Move to front will do the right thing. } else { this._count++; entry = new CacheNode() { Key = key, Value = value, LastAccessed = DateTime.UtcNow }; } _entries.Add(key, entry); } } } else { // If V is a nonprimitive Value type (struct) then sets are // not atomic, therefore we need to lock on the entry. lock (entry) { entry.Value = value; } } MoveToHead(entry); // We don't need to lock here because two threads at this point // can both happily perform this check and set, since they are // both atomic. if (null == this._tail) { this._tail = this._head; } return(true); }