private void UtcUpdateUsageRecursive(CacheEntry entry, DateTime utcNow) { if (((utcNow - entry.UtcLastUsageUpdate) > CacheUsage.CORRELATED_REQUEST_TIMEOUT) || (utcNow < entry.UtcLastUsageUpdate)) { entry.UtcLastUsageUpdate = utcNow; if (entry.InUsage()) { CacheSingle cacheSingle; if (this._cacheMultiple == null) { cacheSingle = this; } else { cacheSingle = this._cacheMultiple.GetCacheSingle(entry.Key.GetHashCode()); } cacheSingle._usage.Update(entry); } CacheDependency dependency = entry.Dependency; if (dependency != null) { CacheEntry[] cacheEntries = dependency.CacheEntries; if (cacheEntries != null) { foreach (CacheEntry entry2 in cacheEntries) { this.UtcUpdateUsageRecursive(entry2, utcNow); } } } } }
void UtcUpdateUsageRecursive(CacheEntry entry, DateTime utcNow) { CacheDependency dependency; CacheEntry[] entries; // Don't update if the last update is less than 1 sec away. This way we'll // avoid over updating the usage in the scenario where a cache makes several // update requests. if (utcNow - entry.UtcLastUsageUpdate > CacheUsage.CORRELATED_REQUEST_TIMEOUT || utcNow < entry.UtcLastUsageUpdate) { entry.UtcLastUsageUpdate = utcNow; if (entry.InUsage()) { CacheSingle cacheSingle; if (_cacheMultiple == null) { cacheSingle = this; } else { cacheSingle = _cacheMultiple.GetCacheSingle(entry.Key.GetHashCode()); } cacheSingle._usage.Update(entry); } dependency = entry.Dependency; if (dependency != null) { entries = dependency.CacheEntries; if (entries != null) { foreach (CacheEntry dependent in entries) { UtcUpdateUsageRecursive(dependent, utcNow); } } } } }
/* * Performs all operations on the cache, with the * exception of Clear. The arguments indicate the type of operation: * * @param key The key of the object. * @param newItem The new entry to be added to the cache. * @param replace Whether or not newEntry should replace an existing object in the cache. * @return The item requested. May be null. */ internal override CacheEntry UpdateCache( CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld) { CacheEntry entry = null; CacheEntry oldEntry = null; bool expired = false; DateTime utcNow; CacheDependency newEntryDependency = null; bool isGet, isAdd; bool removeExpired = false; bool updateExpires = false; DateTime utcNewExpires = DateTime.MinValue; CacheEntry.EntryState entryState = CacheEntry.EntryState.NotInCache; bool newEntryNeedsClose = false; CacheItemRemovedReason newEntryRemovedReason = CacheItemRemovedReason.Removed; valueOld = null; isGet = !replace && newEntry == null; isAdd = !replace && newEntry != null; /* * Perform update of cache data structures in a series to * avoid overlapping locks. * * First, update the hashtable. The hashtable is the place * that guarantees what is in or out of the cache. * * Loop here to remove expired items in a Get or Add, where * we can't otherwise delete an item. */ for (;;) { if (removeExpired) { Debug.Trace("CacheUpdate", "Removing expired item found in Get: " + cacheKey); UpdateCache(cacheKey, null, true, CacheItemRemovedReason.Expired, out valueOld); removeExpired = false; } entry = null; utcNow = DateTime.UtcNow; if (_useInsertBlock && newEntry != null && newEntry.HasUsage() /* HasUsage() means it's not NonRemovable */) { bool insertBlockReleased = WaitInsertBlock(); #if DBG if (!insertBlockReleased) { Debug.Trace("CacheUpdateWaitFailed", "WaitInsertBlock failed."); } #endif } // the _entries hashtable supports multiple readers or one writer bool isLockEntered = false; if (!isGet) { Monitor.Enter(_lock, ref isLockEntered); } try { entry = (CacheEntry) _entries[cacheKey]; Debug.Trace("CacheUpdate", "Entry " + ((entry != null) ? "found" : "not found") + "in hashtable: " + cacheKey); if (entry != null) { entryState = entry.State; // If isGet == true, we are not hold any lock and so entryState can be anything Debug.Assert( isGet || entryState == CacheEntry.EntryState.AddingToCache || entryState == CacheEntry.EntryState.AddedToCache, "entryState == CacheEntry.EntryState.AddingToCache || entryState == CacheEntry.EntryState.AddedToCache"); expired = (_cacheCommon._enableExpiration) && (entry.UtcExpires < utcNow); if (expired) { if (isGet) { /* * If the expired item is Added to the cache, remove it now before * its expiration timer fires up to a minute in the future. * Otherwise, just return null to indicate the item is not available. */ if (entryState == CacheEntry.EntryState.AddedToCache) { removeExpired = true; continue; } entry = null; } else { /* * If it's a call to Add, replace the item * when it has expired. */ replace = true; /* * Change the removed reason. */ removedReason = CacheItemRemovedReason.Expired; } } else { updateExpires = (_cacheCommon._enableExpiration) && (entry.SlidingExpiration > TimeSpan.Zero); } } /* * Avoid running unnecessary code in a Get request by this simple test: */ if (!isGet) { /* * Remove an item from the hashtable. */ if (replace && entry != null) { bool doRemove = (entryState != CacheEntry.EntryState.AddingToCache); if (doRemove) { oldEntry = entry; oldEntry.State = CacheEntry.EntryState.RemovingFromCache; _entries.Remove(oldEntry); Debug.Trace("CacheUpdate", "Entry removed from hashtable: " + cacheKey); } else { /* * If we're removing and couldn't remove the old item * because its state was AddingToCache, return null * to indicate failure. */ if (newEntry == null) { Debug.Trace("CacheUpdate", "Removal from hashtable failed: " + cacheKey); entry = null; } } } /* * Add an item to the hashtable. */ if (newEntry != null) { bool doAdd = true; if (entry != null) { if (oldEntry == null) { /* * We could not remove the existing entry, * either because it simply exists and replace == false, * or replace == true and it's state was AddingToCache when * we tried to remove it. */ doAdd = false; newEntryRemovedReason = CacheItemRemovedReason.Removed; } #if DBG if (!doAdd) { Debug.Trace("CacheUpdate", "Insertion into hashtable failed because old entry was not removed: " + cacheKey); } #endif } if (doAdd) { /* non-definitive check */ newEntryDependency = newEntry.Dependency; if (newEntryDependency != null) { if (newEntryDependency.HasChanged) { doAdd = false; newEntryRemovedReason = CacheItemRemovedReason.DependencyChanged; } #if DBG if (!doAdd) { Debug.Trace("CacheUpdate", "Insertion into hashtable failed because dependency changed: " + cacheKey); } #endif } } if (doAdd) { newEntry.State = CacheEntry.EntryState.AddingToCache; _entries.Add(newEntry, newEntry); /* * If this is an Add operation, indicate success * by returning null. */ if (isAdd) { Debug.Assert(entry == null || expired, "entry == null || expired"); entry = null; } else { /* * Indicate success by returning the inserted entry. */ entry = newEntry; } Debug.Trace("CacheUpdate", "Entry added to hashtable: " + cacheKey); } else { if (!isAdd) { /* * If we failed for an Insert, indicate failure by returning null. */ entry = null; newEntryNeedsClose = true; } else { /* * If we failed for an Add (e.g. Dependency has changed), * return the existing value. If existing value is null, * we have to close the newEntry ourselves. Otherwise, we'll * return non-null and the caller should close the item. */ newEntryNeedsClose = (entry == null); } /* * If newEntry cannot be inserted, and it does not need to be * closed, set it to null so that we don't insert it later. * Leave it non-null when it needs to be closed that that we * can close it. */ if (!newEntryNeedsClose) { newEntry = null; } } } } break; } finally { if (isLockEntered) { Monitor.Exit(_lock); } } } /* * Since we want Get to be fast, check here for a get without * alteration to cache. */ if (isGet) { if (entry != null) { if (updateExpires) { utcNewExpires = utcNow + entry.SlidingExpiration; if (utcNewExpires - entry.UtcExpires >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < entry.UtcExpires) { _expires.UtcUpdate(entry, utcNewExpires); } } UtcUpdateUsageRecursive(entry, utcNow); } if (cacheKey.IsPublic) { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_RATIO_BASE); if (entry != null) { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_HITS); } else { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_MISSES); } } PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_RATIO_BASE); if (entry != null) { PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_HITS); } else { PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_MISSES); } #if DBG if (entry != null) { Debug.Trace("CacheUpdate", "Cache hit: " + cacheKey); } else { Debug.Trace("CacheUpdate", "Cache miss: " + cacheKey); } #endif } else { int totalDelta = 0; int publicDelta = 0; int totalTurnover = 0; int publicTurnover = 0; if (oldEntry != null) { if (oldEntry.InExpires()) { _expires.Remove(oldEntry); } if (oldEntry.InUsage()) { _usage.Remove(oldEntry); } Debug.Assert(oldEntry.State == CacheEntry.EntryState.RemovingFromCache, "oldEntry.State == CacheEntry.EntryState.RemovingFromCache"); oldEntry.State = CacheEntry.EntryState.RemovedFromCache; valueOld = oldEntry.Value; totalDelta--; totalTurnover++; if (oldEntry.IsPublic) { publicDelta--; publicTurnover++; } #if DBG Debug.Trace("CacheUpdate", "Entry removed from cache, reason=" + removedReason + ": " + (CacheKey) oldEntry); #endif } if (newEntry != null) { if (newEntryNeedsClose) { // Call close if newEntry could not be added. newEntry.State = CacheEntry.EntryState.RemovedFromCache; newEntry.Close(newEntryRemovedReason); newEntry = null; } else { Debug.Assert(!newEntry.InExpires()); Debug.Assert(!newEntry.InUsage()); if (_cacheCommon._enableExpiration && newEntry.HasExpiration()) { _expires.Add(newEntry); } if ( _cacheCommon._enableMemoryCollection && newEntry.HasUsage() && ( // Don't bother to set usage if it's going to expire very soon !newEntry.HasExpiration() || newEntry.SlidingExpiration > TimeSpan.Zero || newEntry.UtcExpires - utcNow >= CacheUsage.MIN_LIFETIME_FOR_USAGE)) { _usage.Add(newEntry); } newEntry.State = CacheEntry.EntryState.AddedToCache; Debug.Trace("CacheUpdate", "Entry added to cache: " + (CacheKey)newEntry); totalDelta++; totalTurnover++; if (newEntry.IsPublic) { publicDelta++; publicTurnover++; } } } // Call close after the newEntry has been fully added to the cache, // so the OnRemoveCallback can take a dependency on the newly inserted item. if (oldEntry != null) { oldEntry.Close(removedReason); } // Delay monitoring change events until the oldEntry has been completely removed // from the cache, and its OnRemoveCallback called. This way we won't call the // OnRemoveCallback for newEntry before doing so for oldEntry. if (newEntry != null) { // listen to change events newEntry.MonitorDependencyChanges(); /* * NB: We have to check for dependency changes after we add the item * to cache, because otherwise we may not remove it if it changes * between the time we check for a dependency change and the time * we set the AddedToCache bit. The worst that will happen is that * a get can occur on an item that has changed, but that can happen * anyway. The important thing is that we always remove an item that * has changed. */ if (newEntryDependency != null && newEntryDependency.HasChanged) { Remove(newEntry, CacheItemRemovedReason.DependencyChanged); } } // update counts and counters if (totalDelta == 1) { Interlocked.Increment(ref _totalCount); PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES); } else if (totalDelta == -1) { Interlocked.Decrement(ref _totalCount); PerfCounters.DecrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES); } if (publicDelta == 1) { Interlocked.Increment(ref _publicCount); PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_ENTRIES); } else if (publicDelta == -1) { Interlocked.Decrement(ref _publicCount); PerfCounters.DecrementCounter(AppPerfCounter.API_CACHE_ENTRIES); } if (totalTurnover > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.TOTAL_CACHE_TURNOVER_RATE, totalTurnover); } if (publicTurnover > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.API_CACHE_TURNOVER_RATE, publicTurnover); } } return entry; }
internal override CacheEntry UpdateCache(CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld) { CacheEntry cacheEntry = null; CacheEntry key = null; CacheDependency dependency = null; bool flag4 = false; bool flag5 = false; DateTime minValue = DateTime.MinValue; CacheEntry.EntryState notInCache = CacheEntry.EntryState.NotInCache; bool flag6 = false; CacheItemRemovedReason removed = CacheItemRemovedReason.Removed; valueOld = null; bool flag2 = !replace && (newEntry == null); bool flag3 = !replace && (newEntry != null); Label_003E: if (flag4) { this.UpdateCache(cacheKey, null, true, CacheItemRemovedReason.Expired, out valueOld); flag4 = false; } cacheEntry = null; DateTime utcNow = DateTime.UtcNow; if ((this._useInsertBlock && (newEntry != null)) && newEntry.HasUsage()) { this.WaitInsertBlock(); } bool lockTaken = false; if (!flag2) { Monitor.Enter(this._lock, ref lockTaken); } try { cacheEntry = (CacheEntry)this._entries[cacheKey]; if (cacheEntry != null) { notInCache = cacheEntry.State; if (base._cacheCommon._enableExpiration && (cacheEntry.UtcExpires < utcNow)) { if (flag2) { if (notInCache == CacheEntry.EntryState.AddedToCache) { flag4 = true; goto Label_003E; } cacheEntry = null; } else { replace = true; removedReason = CacheItemRemovedReason.Expired; } } else { flag5 = base._cacheCommon._enableExpiration && (cacheEntry.SlidingExpiration > TimeSpan.Zero); } } if (!flag2) { if (replace && (cacheEntry != null)) { if (notInCache != CacheEntry.EntryState.AddingToCache) { key = cacheEntry; key.State = CacheEntry.EntryState.RemovingFromCache; this._entries.Remove(key); } else if (newEntry == null) { cacheEntry = null; } } if (newEntry != null) { bool flag9 = true; if ((cacheEntry != null) && (key == null)) { flag9 = false; removed = CacheItemRemovedReason.Removed; } if (flag9) { dependency = newEntry.Dependency; if ((dependency != null) && dependency.HasChanged) { flag9 = false; removed = CacheItemRemovedReason.DependencyChanged; } } if (flag9) { newEntry.State = CacheEntry.EntryState.AddingToCache; this._entries.Add(newEntry, newEntry); if (flag3) { cacheEntry = null; } else { cacheEntry = newEntry; } } else { if (!flag3) { cacheEntry = null; flag6 = true; } else { flag6 = cacheEntry == null; } if (!flag6) { newEntry = null; } } } } } finally { if (lockTaken) { Monitor.Exit(this._lock); } } if (flag2) { if (cacheEntry != null) { if (flag5) { minValue = utcNow + cacheEntry.SlidingExpiration; if (((minValue - cacheEntry.UtcExpires) >= CacheExpires.MIN_UPDATE_DELTA) || (minValue < cacheEntry.UtcExpires)) { this._expires.UtcUpdate(cacheEntry, minValue); } } this.UtcUpdateUsageRecursive(cacheEntry, utcNow); } if (cacheKey.IsPublic) { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_RATIO_BASE); if (cacheEntry != null) { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_HITS); } else { PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_MISSES); } } PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_RATIO_BASE); if (cacheEntry != null) { PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_HITS); return(cacheEntry); } PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_MISSES); return(cacheEntry); } int num = 0; int num2 = 0; int delta = 0; int num4 = 0; if (key != null) { if (key.InExpires()) { this._expires.Remove(key); } if (key.InUsage()) { this._usage.Remove(key); } key.State = CacheEntry.EntryState.RemovedFromCache; valueOld = key.Value; num--; delta++; if (key.IsPublic) { num2--; num4++; } } if (newEntry != null) { if (flag6) { newEntry.State = CacheEntry.EntryState.RemovedFromCache; newEntry.Close(removed); newEntry = null; } else { if (base._cacheCommon._enableExpiration && newEntry.HasExpiration()) { this._expires.Add(newEntry); } if ((base._cacheCommon._enableMemoryCollection && newEntry.HasUsage()) && ((!newEntry.HasExpiration() || (newEntry.SlidingExpiration > TimeSpan.Zero)) || ((newEntry.UtcExpires - utcNow) >= CacheUsage.MIN_LIFETIME_FOR_USAGE))) { this._usage.Add(newEntry); } newEntry.State = CacheEntry.EntryState.AddedToCache; num++; delta++; if (newEntry.IsPublic) { num2++; num4++; } } } if (key != null) { key.Close(removedReason); } if (newEntry != null) { newEntry.MonitorDependencyChanges(); if ((dependency != null) && dependency.HasChanged) { base.Remove(newEntry, CacheItemRemovedReason.DependencyChanged); } } switch (num) { case 1: Interlocked.Increment(ref this._totalCount); PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES); break; case -1: Interlocked.Decrement(ref this._totalCount); PerfCounters.DecrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES); break; } switch (num2) { case 1: Interlocked.Increment(ref this._publicCount); PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_ENTRIES); break; case -1: Interlocked.Decrement(ref this._publicCount); PerfCounters.DecrementCounter(AppPerfCounter.API_CACHE_ENTRIES); break; } if (delta > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.TOTAL_CACHE_TURNOVER_RATE, delta); } if (num4 > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.API_CACHE_TURNOVER_RATE, num4); } return(cacheEntry); }
private void UtcUpdateUsageRecursive(CacheEntry entry, DateTime utcNow) { if (((utcNow - entry.UtcLastUsageUpdate) > CacheUsage.CORRELATED_REQUEST_TIMEOUT) || (utcNow < entry.UtcLastUsageUpdate)) { entry.UtcLastUsageUpdate = utcNow; if (entry.InUsage()) { CacheSingle cacheSingle; if (this._cacheMultiple == null) { cacheSingle = this; } else { cacheSingle = this._cacheMultiple.GetCacheSingle(entry.Key.GetHashCode()); } cacheSingle._usage.Update(entry); } CacheDependency dependency = entry.Dependency; if (dependency != null) { CacheEntry[] cacheEntries = dependency.CacheEntries; if (cacheEntries != null) { foreach (CacheEntry entry2 in cacheEntries) { this.UtcUpdateUsageRecursive(entry2, utcNow); } } } } }