// // System.Web.Caching // // Author: // Patrik Torstensson (Patrik.Torstensson@labs2.com) // // (C) Copyright Patrik Torstensson, 2001 // namespace System.Web.Caching { /// /// Responsible for holding a cache entry in the linked list bucket. /// public struct ExpiresEntry { public CacheEntry _objEntry; public long _ticksExpires; public int _intNext; } /// /// Holds cache entries that has a expiration in a bucket list. /// public class ExpiresBucket { private static int MIN_ENTRIES = 16; private byte _byteID; private int _intSize; private int _intCount; private int _intNext; private Cache _objManager; private ExpiresEntry [] _arrEntries; /// /// Constructs a new bucket. /// /// Current bucket ID. /// Cache manager reponsible for the item(s) in the expires bucket. public ExpiresBucket(byte bucket, Cache objManager) { _objManager = objManager; Initialize(bucket); } /// /// Initializes the expires bucket, creates a linked list of MIN_ENTRIES. /// /// Bucket ID. private void Initialize(byte bucket) { _byteID = bucket; _intNext = 0; _intCount = 0; _arrEntries = new ExpiresEntry[MIN_ENTRIES]; _intSize = MIN_ENTRIES; int intPos = 0; do { _arrEntries[intPos]._intNext = intPos + 1; _arrEntries[intPos]._ticksExpires = System.DateTime.MaxValue.Ticks; intPos++; } while (intPos < _intSize); _arrEntries[_intSize - 1]._intNext = -1; } /// /// Expands the bucket linked array list. /// private void Expand() { ExpiresEntry [] arrData; int intPos = 0; int intOldSize; lock(this) { intOldSize = _intSize; _intSize *= 2; // Create a new array and copy the old data into the new array arrData = new ExpiresEntry[_intSize]; do { arrData[intPos] = _arrEntries[intPos]; intPos++; } while (intPos < intOldSize); _intNext = intPos; // Initialize the "new" positions. do { arrData[intPos]._intNext = intPos + 1; intPos++; } while (intPos < _intSize); arrData[_intSize - 1]._intNext = -1; _arrEntries = arrData; } } /// /// Adds a cache entry into the expires bucket. /// /// Cache Entry object to be added. public void Add(CacheEntry objEntry) { if (_intNext == -1) { Expand(); } lock(this) { _arrEntries[_intNext]._ticksExpires = objEntry.Expires; _arrEntries[_intNext]._objEntry = objEntry; _intNext = _arrEntries[_intNext]._intNext; _intCount++; } } /// /// Removes a cache entry from the expires bucket. /// /// Cache entry to be removed. public void Remove(CacheEntry objEntry) { lock(this) { // Check if this is our bucket if (objEntry.ExpiresIndex != _byteID) return; if (objEntry.ExpiresIndex == System.Int32.MaxValue) return; if (_arrEntries.Length < objEntry.ExpiresIndex) return; _intCount--; _arrEntries[objEntry.ExpiresIndex]._objEntry.ExpiresBucket = byte.MaxValue; _arrEntries[objEntry.ExpiresIndex]._objEntry.ExpiresIndex = int.MaxValue; _arrEntries[objEntry.ExpiresIndex]._objEntry = null; _intNext = _arrEntries[objEntry.ExpiresIndex]._intNext; } } /// /// Updates a cache entry in the expires bucket, this is called during a hit of an item if the /// cache item has a sliding expiration. The function is responsible for updating the cache /// entry. /// /// Cache entry to update. /// New expiration value for the cache entry. public void Update(CacheEntry objEntry, long ticksExpires) { lock(this) { // Check if this is our bucket if (objEntry.ExpiresIndex != _byteID) return; if (objEntry.ExpiresIndex == System.Int32.MaxValue) return; if (_arrEntries.Length < objEntry.ExpiresIndex) return; _arrEntries[objEntry.ExpiresIndex]._ticksExpires = ticksExpires; _arrEntries[objEntry.ExpiresIndex]._objEntry.Expires = ticksExpires; } } /// /// Flushes all cache entries that has expired and removes them from the cache manager. /// public void FlushExpiredItems() { ExpiresEntry objEntry; CacheEntry [] arrCacheEntries; int intCachePos; int intPos; long ticksNow; ticksNow = System.DateTime.Now.Ticks; intCachePos = 0; // Lookup all items that needs to be removed, this is done in a two part // operation to minimize the locking time. lock (this) { arrCacheEntries = new CacheEntry[_intSize]; intPos = 0; do { objEntry = _arrEntries[intPos]; if (objEntry._objEntry != null) { if (objEntry._ticksExpires < ticksNow) { arrCacheEntries[intCachePos++] = objEntry._objEntry; objEntry._objEntry.ExpiresBucket = byte.MaxValue; objEntry._objEntry.ExpiresIndex = int.MaxValue; objEntry._objEntry = null; _intNext = objEntry._intNext; } } intPos++; } while (intPos < _intSize); } // If we have any entries to remove, go ahead and call the cache manager remove. if (intCachePos > 0) { intPos = 0; do { _objManager.Remove(arrCacheEntries[intPos].Key, CacheItemRemovedReason.Expired); intPos++; } while (intPos < intCachePos); } } /// /// Returns the current size of the expires bucket. /// public int Size { get { return _arrEntries.Length; } } /// /// Returns number of items in the bucket. /// public int Count { get { return _intCount; } } } }