internal void UtcUpdateCacheEntry(MemoryCacheEntry cacheEntry, DateTime utcExpires) { lock (this) { ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef; if (cacheEntry.ExpiresBucket != _bucket || entryRef.IsInvalid) { return; } ExpiresEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); int entryIndex = entryRef.Index; Dbg.Assert(cacheEntry == entries[entryIndex]._cacheEntry); RemoveCount(entries[entryIndex]._utcExpires); AddCount(utcExpires); entries[entryIndex]._utcExpires = utcExpires; cacheEntry.UtcAbsExp = utcExpires; Dbg.Validate("CacheValidateExpires", this); Dbg.Trace("CacheExpiresUpdate", "Updated item " + cacheEntry.Key + " in bucket " + _bucket); } }
private void RemoveFromCache(MemoryCacheEntry entry, CacheEntryRemovedReason reason, bool delayRelease = false) { // release outside of lock if (entry != null) { if (entry.InExpires()) { _expires.Remove(entry); } if (entry.InUsage()) { _usage.Remove(entry); } Dbg.Assert(entry.State == EntryState.RemovingFromCache, "entry.State = EntryState.RemovingFromCache"); entry.State = EntryState.RemovedFromCache; if (!delayRelease) { entry.Release(_cache, reason); } if (_perfCounters != null) { _perfCounters.Decrement(PerfCounterName.Entries); _perfCounters.Increment(PerfCounterName.Turnover); } } }
private void RemoveFromList(int pageIndex, ref ExpiresPageList list) { Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); if ((_pages[(pageIndex)]._pagePrev) != -1) { Dbg.Assert((_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) == pageIndex, "PageNext(PagePrev(pageIndex)) == pageIndex"); (_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) = (_pages[(pageIndex)]._pageNext); } else { Dbg.Assert(list._head == pageIndex, "list._head == pageIndex"); list._head = (_pages[(pageIndex)]._pageNext); } if ((_pages[(pageIndex)]._pageNext) != -1) { Dbg.Assert((_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) == pageIndex, "PagePrev(PageNext(pageIndex)) == pageIndex"); (_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) = (_pages[(pageIndex)]._pagePrev); } else { Dbg.Assert(list._tail == pageIndex, "list._tail == pageIndex"); list._tail = (_pages[(pageIndex)]._pagePrev); } (_pages[(pageIndex)]._pagePrev) = -1; (_pages[(pageIndex)]._pageNext) = -1; }
internal MemoryCacheEntryChangeMonitor(ReadOnlyCollection <String> keys, String regionName, MemoryCache cache) { Dbg.Assert(keys != null && keys.Count > 0, "keys != null && keys.Count > 0"); _keys = keys; _regionName = regionName; InitDisposableMembers(cache); }
private void AddUsageEntryToFreeList(UsageEntryRef entryRef) { Dbg.Assert(entryRef.IsRef1, "entryRef.IsRef1"); UsageEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); int entryIndex = entryRef.Ref1Index; Dbg.Assert(entries[entryIndex]._cacheEntry == null, "entries[entryIndex]._cacheEntry == null"); entries[entryIndex]._utcDate = DateTime.MinValue; entries[entryIndex]._ref1._prev = UsageEntryRef.INVALID; entries[entryIndex]._ref2._next = UsageEntryRef.INVALID; entries[entryIndex]._ref2._prev = UsageEntryRef.INVALID; entries[entryIndex]._ref1._next = ((entries)[0]._ref1._next); ((entries)[0]._ref1._next) = entryRef; _cEntriesInUse--; int pageIndex = entryRef.PageIndex; ((entries)[0]._cFree)++; if (((entries)[0]._cFree) == 1) { AddToListHead(pageIndex, ref _freeEntryList); } else if (((entries)[0]._cFree) == NUM_ENTRIES) { RemovePage(pageIndex); } }
internal void Add(MemoryCacheEntry cacheEntry) { byte bucket = cacheEntry.UsageBucket; Dbg.Assert(bucket != 0xff, "bucket != 0xff"); _buckets[bucket].AddCacheEntry(cacheEntry); }
internal ExpiresEntryRef(int pageIndex, int entryIndex) { Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex"); Dbg.Assert((entryIndex & ENTRY_MASK) == entryIndex, "(entryIndex & ENTRY_MASK) == entryIndex"); Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0"); _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK)); }
internal UsageEntryRef(int pageIndex, int entryIndex) { Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex"); Dbg.Assert((Math.Abs(entryIndex) & ENTRY_MASK) == (Math.Abs(entryIndex)), "(Math.Abs(entryIndex) & ENTRY_MASK) == Math.Abs(entryIndex)"); Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0"); _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK)); }
private int RemoveFromListHead(ref ExpiresPageList list) { Dbg.Assert(list._head != -1, "list._head != -1"); int oldHead = list._head; RemoveFromList(oldHead, ref list); return(oldHead); }
protected override int GetCurrentPressure() { // Call GetUpdatedTotalCacheSize to update the total // cache size, if there has been a recent Gen 2 Collection. // This update must happen, otherwise the CacheManager won't // know the total cache size. int gen2Count = GC.CollectionCount(2); SRefMultiple sref = _sizedRefMultiple; if (gen2Count != _gen2Count && sref != null) { // update _gen2Count _gen2Count = gen2Count; // the SizedRef is only updated after a Gen2 Collection // increment the index (it's either 1 or 0) Dbg.Assert(SAMPLE_COUNT == 2); _idx = _idx ^ 1; // remember the sample time _cacheSizeSampleTimes[_idx] = DateTime.UtcNow; // remember the sample value _cacheSizeSamples[_idx] = sref.ApproximateSize; #if DBG Dbg.Trace("MemoryCacheStats", "SizedRef.ApproximateSize=" + _cacheSizeSamples[_idx]); #endif IMemoryCacheManager memoryCacheManager = s_memoryCacheManager; if (memoryCacheManager != null) { memoryCacheManager.UpdateCacheSize(_cacheSizeSamples[_idx], _memoryCache); } } // if there's no memory limit, then there's nothing more to do if (_memoryLimit <= 0) { return(0); } long cacheSize = _cacheSizeSamples[_idx]; // use _memoryLimit as an upper bound so that pressure is a percentage (between 0 and 100, inclusive). if (cacheSize > _memoryLimit) { cacheSize = _memoryLimit; } // PerfCounter: Cache Percentage Process Memory Limit Used // = memory used by this process / process memory limit at pressureHigh // Set private bytes used in kilobytes because the counter is a DWORD // int result = (int)(cacheSize * 100 / _memoryLimit); return(result); }
// Derived classes must call InitializationComplete protected void InitializationComplete() { _flags[INITIALIZED] = true; // If the dependency has already changed, or someone tried to dispose us, then call Dispose now. Dbg.Assert(_flags[INITIALIZED], "It is critical that INITIALIZED is set before CHANGED is checked below"); if (_flags[CHANGED]) { Dispose(); } }
// Derived classes call OnChanged when the dependency changes. Optionally, // they may pass state which will be passed to the OnChangedCallback. The // OnChangedCallback is only invoked once, and only after NotifyOnChanged is // called by the cache implementer. OnChanged is also invoked when the instance // is disposed, but only has an affect if the callback has not already been invoked. protected void OnChanged(Object state) { OnChangedHelper(state); // OnChanged will also invoke Dispose, but only after initialization is complete Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below."); if (_flags[INITIALIZED]) { DisposeHelper(); } }
bool IEqualityComparer.Equals(Object x, Object y) { Dbg.Assert(x != null && x is MemoryCacheKey); Dbg.Assert(y != null && y is MemoryCacheKey); MemoryCacheKey a, b; a = (MemoryCacheKey)x; b = (MemoryCacheKey)y; return(String.Compare(a.Key, b.Key, StringComparison.Ordinal) == 0); }
internal long TrimInternal(int percent) { Dbg.Assert(percent <= 100, "percent <= 100"); int count = Count; int toTrim = 0; // do we need to drop a percentage of entries? if (percent > 0) { toTrim = (int)Math.Ceiling(((long)count * (long)percent) / 100D); // would this leave us above MAX_COUNT? int minTrim = count - MAX_COUNT; if (toTrim < minTrim) { toTrim = minTrim; } } // do we need to trim? if (toTrim <= 0 || _disposed == 1) { return(0); } int trimmed = 0; // total number of entries trimmed int trimmedOrExpired = 0; #if DEBUG int beginTotalCount = count; #endif trimmedOrExpired = _expires.FlushExpiredItems(true); if (trimmedOrExpired < toTrim) { trimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired); trimmedOrExpired += trimmed; } if (trimmed > 0 && _perfCounters != null) { // Update values for perfcounters _perfCounters.IncrementBy(PerfCounterName.Trims, trimmed); } #if DEBUG Dbg.Trace("MemoryCacheStore", "TrimInternal:" + " beginTotalCount=" + beginTotalCount + ", endTotalCount=" + count + ", percent=" + percent + ", trimmed=" + trimmed); #endif return(trimmedOrExpired); }
public void Dispose() { OnChangedHelper(null); // If not initialized, throw, so the derived class understands that it must call InitializeComplete before Dispose. Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below."); if (!_flags[INITIALIZED]) { throw new InvalidOperationException(SR.Init_not_complete); } DisposeHelper(); }
internal CacheExpires(MemoryCacheStore cacheStore) { Dbg.Assert(NUMBUCKETS < Byte.MaxValue); DateTime utcNow = DateTime.UtcNow; _cacheStore = cacheStore; _buckets = new ExpiresBucket[NUMBUCKETS]; for (byte b = 0; b < _buckets.Length; b++) { _buckets[b] = new ExpiresBucket(this, b, utcNow); } }
private void MoveToListHead(int pageIndex, ref UsagePageList list) { Dbg.Assert(list._head != -1, "list._head != -1"); Dbg.Assert(list._tail != -1, "list._tail != -1"); if (list._head == pageIndex) { return; } RemoveFromList(pageIndex, ref list); AddToListHead(pageIndex, ref list); }
private void MoveToListTail(int pageIndex, ref ExpiresPageList list) { Dbg.Assert(list._head != -1, "list._head != -1"); Dbg.Assert(list._tail != -1, "list._tail != -1"); if (list._tail == pageIndex) { return; } RemoveFromList(pageIndex, ref list); AddToListTail(pageIndex, ref list); }
private void InitZeroPages() { Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0"); Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0"); Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); _pages = null; _minEntriesInUse = -1; _freePageList._head = -1; _freePageList._tail = -1; _freeEntryList._head = -1; _freeEntryList._tail = -1; }
protected void InitHistory() { Dbg.Assert(_pressureHigh > 0, "_pressureHigh > 0"); Dbg.Assert(_pressureLow > 0, "_pressureLow > 0"); Dbg.Assert(_pressureLow <= _pressureHigh, "_pressureLow <= _pressureHigh"); int pressure = GetCurrentPressure(); _pressureHist = new int[HISTORY_COUNT]; for (int i = 0; i < HISTORY_COUNT; i++) { _pressureHist[i] = pressure; _pressureTotal += pressure; } }
private void InitZeroPages() { Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0"); Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0"); Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); Dbg.Assert(_lastRefHead.IsInvalid, "_lastRefHead.IsInvalid"); Dbg.Assert(_lastRefTail.IsInvalid, "_lastRefTail.IsInvalid"); Dbg.Assert(_addRef2Head.IsInvalid, "_addRef2Head.IsInvalid"); _pages = null; _minEntriesInUse = -1; _freePageList._head = -1; _freePageList._tail = -1; _freeEntryList._head = -1; _freeEntryList._tail = -1; }
internal PhysicalMemoryMonitor(int physicalMemoryLimitPercentage) { /* * The chart below shows physical memory in megabytes, and the 1, 3, and 10% values. * When we reach "middle" pressure, we begin trimming the cache. * * RAM 1% 3% 10% * ----------------------------- * 128 1.28 3.84 12.8 * 256 2.56 7.68 25.6 * 512 5.12 15.36 51.2 * 1024 10.24 30.72 102.4 * 2048 20.48 61.44 204.8 * 4096 40.96 122.88 409.6 * 8192 81.92 245.76 819.2 * */ long memory = TotalPhysical; Dbg.Assert(memory != 0, "memory != 0"); if (memory >= 0x100000000) { _pressureHigh = 99; } else if (memory >= 0x80000000) { _pressureHigh = 98; } else if (memory >= 0x40000000) { _pressureHigh = 97; } else if (memory >= 0x30000000) { _pressureHigh = 96; } else { _pressureHigh = 95; } _pressureLow = _pressureHigh - 9; SetLimit(physicalMemoryLimitPercentage); InitHistory(); }
private ExpiresEntryRef GetFreeExpiresEntry() { Dbg.Assert(_freeEntryList._head >= 0, "_freeEntryList._head >= 0"); int pageIndex = _freeEntryList._head; ExpiresEntry[] entries = (_pages[(pageIndex)]._entries); int entryIndex = ((entries)[0]._next).Index; ((entries)[0]._next) = entries[entryIndex]._next; ((entries)[0]._cFree)--; if (((entries)[0]._cFree) == 0) { Dbg.Assert(((entries)[0]._next).IsInvalid, "FreeEntryHead(entries).IsInvalid"); RemoveFromList(pageIndex, ref _freeEntryList); } return(new ExpiresEntryRef(pageIndex, entryIndex)); }
private void AddToListHead(int pageIndex, ref UsagePageList list) { Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); (_pages[(pageIndex)]._pagePrev) = -1; (_pages[(pageIndex)]._pageNext) = list._head; if (list._head != -1) { Dbg.Assert((_pages[(list._head)]._pagePrev) == -1, "PagePrev(list._head) == -1"); (_pages[(list._head)]._pagePrev) = pageIndex; } else { list._tail = pageIndex; } list._head = pageIndex; }
private void AddToListTail(int pageIndex, ref ExpiresPageList list) { Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); (_pages[(pageIndex)]._pageNext) = -1; (_pages[(pageIndex)]._pagePrev) = list._tail; if (list._tail != -1) { Dbg.Assert((_pages[(list._tail)]._pageNext) == -1, "PageNext(list._tail) == -1"); (_pages[(list._tail)]._pageNext) = pageIndex; } else { list._head = pageIndex; } list._tail = pageIndex; }
internal void AddCacheEntry(MemoryCacheEntry cacheEntry) { lock (this) { if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0) { return; } ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef; Dbg.Assert((cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid, "(cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid"); if (cacheEntry.ExpiresBucket != 0xff || !entryRef.IsInvalid) { return; } if (_freeEntryList._head == -1) { Expand(); } ExpiresEntryRef freeRef = GetFreeExpiresEntry(); Dbg.Assert(cacheEntry.ExpiresBucket == 0xff, "cacheEntry.ExpiresBucket == 0xff"); Dbg.Assert(cacheEntry.ExpiresEntryRef.IsInvalid, "cacheEntry.ExpiresEntryRef.IsInvalid"); cacheEntry.ExpiresBucket = _bucket; cacheEntry.ExpiresEntryRef = freeRef; ExpiresEntry[] entries = (_pages[(freeRef.PageIndex)]._entries); int entryIndex = freeRef.Index; entries[entryIndex]._cacheEntry = cacheEntry; entries[entryIndex]._utcExpires = cacheEntry.UtcAbsExp; AddCount(cacheEntry.UtcAbsExp); _cEntriesInUse++; if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0) { RemoveCacheEntryNoLock(cacheEntry); } } }
private void UpdateMinEntries() { if (_cPagesInUse <= 1) { _minEntriesInUse = -1; } else { int capacity = _cPagesInUse * NUM_ENTRIES; Dbg.Assert(capacity > 0, "capacity > 0"); Dbg.Assert(MIN_LOAD_FACTOR < 1.0, "MIN_LOAD_FACTOR < 1.0"); _minEntriesInUse = (int)(capacity * MIN_LOAD_FACTOR); if ((_minEntriesInUse - 1) > ((_cPagesInUse - 1) * NUM_ENTRIES)) { _minEntriesInUse = -1; } } }
private void RemovePage(int pageIndex) { Dbg.Assert((((_pages[(pageIndex)]._entries))[0]._cFree) == NUM_ENTRIES, "FreeEntryCount(EntriesI(pageIndex)) == NUM_ENTRIES"); RemoveFromList(pageIndex, ref _freeEntryList); AddToListHead(pageIndex, ref _freePageList); Dbg.Assert((_pages[(pageIndex)]._entries) != null, "EntriesI(pageIndex) != null"); (_pages[(pageIndex)]._entries) = null; _cPagesInUse--; if (_cPagesInUse == 0) { InitZeroPages(); } else { UpdateMinEntries(); } }
internal void Release(MemoryCache cache, CacheEntryRemovedReason reason) { State = EntryState.Closed; // Are there any cache entries that depend on this entry? // If so, we need to fire their dependencies. Dictionary <MemoryCacheEntryChangeMonitor, MemoryCacheEntryChangeMonitor> .KeyCollection deps = null; // clone the dependents lock (this) { if (_fields != null && _fields._dependents != null && _fields._dependents.Count > 0) { deps = _fields._dependents.Keys; // set to null so RemoveDependent does not attempt to access it, since we're not // using a copy of the KeyCollection. _fields._dependents = null; Dbg.Assert(_fields._dependents == null, "_fields._dependents == null"); } } if (deps != null) { foreach (MemoryCacheEntryChangeMonitor dependent in deps) { if (dependent != null) { dependent.OnCacheEntryReleased(); } } } CallCacheEntryRemovedCallback(cache, reason); // Dispose any dependencies if (_fields != null && _fields._dependencies != null) { foreach (ChangeMonitor monitor in _fields._dependencies) { monitor.Dispose(); } } }
public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) == 0) { // disable CacheExpires timer _expires.EnableExpirationTimer(false); // build array list of entries ArrayList entries = new ArrayList(_entries.Count); lock (_entriesLock) { foreach (DictionaryEntry e in _entries) { MemoryCacheEntry entry = e.Value as MemoryCacheEntry; entries.Add(entry); } foreach (MemoryCacheEntry entry in entries) { MemoryCacheKey key = entry as MemoryCacheKey; entry.State = EntryState.RemovingFromCache; _entries.Remove(key); } } // release entries outside of lock foreach (MemoryCacheEntry entry in entries) { RemoveFromCache(entry, CacheEntryRemovedReason.CacheSpecificEviction); } // MemoryCacheStatistics has been disposed, and therefore nobody should be using // _insertBlock except for potential threads in WaitInsertBlock (which won't care if we call Close). Dbg.Assert(_useInsertBlock == false, "_useInsertBlock == false"); _insertBlock.Close(); // Don't need to call GC.SuppressFinalize(this) for sealed types without finalizers. } }