/// <summary> /// Adds a Cache entry to the correct flush bucket. /// </summary> /// <param name="objEntry">Cache entry to add.</param> internal void Add (CacheEntry objEntry) { long now = DateTime.UtcNow.Ticks; if (objEntry.Expires < now) objEntry.Expires = now; _arrBuckets [GetHashBucket (objEntry.Expires)].Add (objEntry); }
protected override void Dispose(bool disposing) { if (disposing && (Interlocked.Exchange(ref this._disposed, 1) == 0)) { if (this._expires != null) { this._expires.EnableExpirationTimer(false); } CacheEntry[] entryArray = null; lock (this._lock) { entryArray = new CacheEntry[this._entries.Count]; int num = 0; foreach (DictionaryEntry entry in this._entries) { entryArray[num++] = (CacheEntry) entry.Value; } } foreach (CacheEntry entry2 in entryArray) { base.Remove(entry2, CacheItemRemovedReason.Removed); } this._insertBlock.Set(); this.ReleaseInsertBlock(); } base.Dispose(disposing); }
internal void AddCacheEntry(CacheEntry cacheEntry) { lock (this) { if (((byte) (cacheEntry.State & (CacheEntry.EntryState.AddedToCache | CacheEntry.EntryState.AddingToCache))) != 0) { ExpiresEntryRef expiresEntryRef = cacheEntry.ExpiresEntryRef; if ((cacheEntry.ExpiresBucket == 0xff) && expiresEntryRef.IsInvalid) { if (this._freeEntryList._head == -1) { this.Expand(); } ExpiresEntryRef freeExpiresEntry = this.GetFreeExpiresEntry(); cacheEntry.ExpiresBucket = this._bucket; cacheEntry.ExpiresEntryRef = freeExpiresEntry; ExpiresEntry[] entryArray = this._pages[freeExpiresEntry.PageIndex]._entries; int index = freeExpiresEntry.Index; entryArray[index]._cacheEntry = cacheEntry; entryArray[index]._utcExpires = cacheEntry.UtcExpires; this.AddCount(cacheEntry.UtcExpires); this._cEntriesInUse++; if (((byte) (cacheEntry.State & (CacheEntry.EntryState.AddedToCache | CacheEntry.EntryState.AddingToCache))) == 0) { this.RemoveCacheEntryNoLock(cacheEntry); } } } } }
internal void Update(CacheEntry cacheEntry) { byte usageBucket = cacheEntry.UsageBucket; if (usageBucket != 0xff) { this._buckets[usageBucket].UpdateCacheEntry(cacheEntry); } }
internal void Add(CacheEntry cacheEntry) { DateTime utcNow = DateTime.UtcNow; if (utcNow > cacheEntry.UtcExpires) { cacheEntry.UtcExpires = utcNow; } int index = this.UtcCalcExpiresBucket(cacheEntry.UtcExpires); this._buckets[index].AddCacheEntry(cacheEntry); }
/* * Dispose the cache. */ protected override void Dispose(bool disposing) { if (disposing) { if (Interlocked.Exchange(ref _disposed, 1) == 0) { if (_expires != null) { _expires.EnableExpirationTimer(false); } // close all items CacheEntry[] entries = null; lock (_lock) { entries = new CacheEntry[_entries.Count]; int i = 0; foreach (DictionaryEntry d in _entries) { entries[i++] = (CacheEntry) d.Value; } } foreach (CacheEntry entry in entries) { Remove(entry, CacheItemRemovedReason.Removed); } // force any waiters to complete their waits. Note // that the insert block cannot be reacquired, as UseInsertBlock // checks the _disposed field. _insertBlock.Set(); // release the block, causing it to be disposed when there // are no more callers. ReleaseInsertBlock(); Debug.Trace("CacheDispose", "Cache disposed"); } } base.Dispose(disposing); }
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); } } } } }
/// <summary> /// Tries to load script from ASP.NET Temporary files - this is useful when /// web is not precompiled (so it is compiled into SSAs) and appdomain is reloaded /// (which means that we loose the cache) /// </summary> private bool TryLoadTemporaryCompiledNoLock(string ns, PhpSourceFile/*!*/ sourceFile, out CacheEntry cache_entry) { CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); string name = WebCompilationContext.GetAssemblyCodedName(sourceFile, config); string sourcePath = sourceFile.FullPath.ToString(); bool sourceExists = File.Exists(sourcePath); DateTime sourceTime = sourceExists ? File.GetLastWriteTime(sourcePath) : DateTime.UtcNow.AddYears(1); // If file does not exist, fake the sourceTime to NOT load any SSA DLL. Delete them instead. DateTime configTime = Configuration.LastConfigurationModificationTime; // here find the max modification of all dependant files (configuration, script itself, other DLLs): long sourceStamp = Math.Max(Math.Max(sourceTime.Ticks, configTime.Ticks), appCodeAssemblyCreated.Ticks); // Find specified file in temporary files if (Directory.Exists(outDir)) foreach (string file in Directory.GetFiles(outDir, name + "*.dll")) { Match match = reFileStamp.Match(file); if (!match.Success) continue; long fileStamp; if (!Int64.TryParse((string)match.Groups["Stamp"].Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out fileStamp)) continue; // File is up-to-date if (sourceStamp < fileStamp) { Debug.WriteLine("WSSM", "Loading from ASP.NET Temporary files."); return LoadSSA(ns, file, out cache_entry); } else { // do "some" cleanup: try { File.Delete(file); File.Delete(Path.ChangeExtension(file, ".pdb")); } catch{ /*nop*/ } } } cache_entry = default(CacheEntry); return false; }
/// <summary> /// Recursive function that loads (SSA) assembly and all included assemblies /// (included assemblies are required because we need to check whether included files are up-to-date) /// </summary> /// <param name="ns">Namespace of the script to be loaded (namespace is encoded file name)</param> /// <param name="type">Type of the <Script> class</param> /// <param name="module">Module of the type - used for token resolving </param> /// <param name="checkStamp">Should we check timestamp?</param> /// <param name="includer">Namespace of the includer (can be null)</param> /// <param name="tempCache">Temporary cache - used only while loading</param> /// <returns>Success?</returns> private bool LoadIncludeesRecursive(string/*!*/ ns, Type type/*!*/, Module/*!*/ module, bool checkStamp, string includer, Dictionary<string, CacheEntry>/*!*/ tempCache) { //File already processed? if (tempCache.ContainsKey(ns)) return true; tempCache[ns] = null; // just recursion prevention // find [Script] attribute ScriptAttribute script_attr = ScriptAttribute.Reflect(type); if (script_attr == null) return false; // check source file timestamp if (checkStamp) { string path = ScriptModule.GetPathFromSubnamespace(ns). ToFullPath(Configuration.Application.Compiler.SourceRoot).ToString(); DateTime writeStamp = File.GetLastWriteTime(path); // note: it does not fail if the file does not exists, in such case it returns 12:00 midnight, January 1, 1601 A.D. if (writeStamp > script_attr.SourceTimestamp) return false; } // find [ScriptIncludees] attribute ScriptIncludeesAttribute script_includees = ScriptIncludeesAttribute.Reflect(type); string[] inclusionNames; if (script_includees != null) { Type[] inclusionScripts; inclusionNames = new string[script_includees.Inclusions.Length]; inclusionScripts = new Type[script_includees.Inclusions.Length]; // resolve included Scripts tokens: for (int i = 0; i < inclusionNames.Length; i++) { try { inclusionScripts[i] = module.ResolveType(script_includees.Inclusions[i]); ScriptAttribute sa = ScriptAttribute.Reflect(inclusionScripts[i]); if (sa == null) return false; inclusionNames[i] = ScriptModule.GetSubnamespace(new RelativePath(sa.RelativePath), false); } catch (ArgumentException) { return false; } } // Try to load all included scripts and check whether files weren't changed for (int i = 0; i < inclusionNames.Length; i++) { if (!LoadIncludeesRecursive(inclusionNames[i], inclusionScripts[i], inclusionScripts[i].Module, true, ns, tempCache)) return false; } } else { inclusionNames = ArrayUtils.EmptyStrings; } // Load SSA assembly SingleScriptAssembly ssa = ScriptAssembly.LoadFromAssembly(applicationContext, type.Assembly) as SingleScriptAssembly; if (ssa != null) { // Save only to temp cache (other calls to LoadIncludeesRecursive may fail!) string[] includers = includer == null ? (ArrayUtils.EmptyStrings) : (new string[] { includer }); CacheEntry entry = new CacheEntry(type, ssa, script_attr.SourceTimestamp, includers, inclusionNames, true); tempCache[ns] = entry; } else { // script in MSA was included from SSA, MSA scripts should not be in cache[] // leave null in tempCache[ns] (as recursion prevention), it will not process into cache[] } return true; }
private void RemoveCachedEntryNoLock(string/*!*/ ns, CacheEntry entry, List<ScriptAssembly> removedAssemblies) { if (cache.Remove(ns)) { Debug.WriteLine("WATCHER", "Cache entry '{0}' removed.", ns); if (removedAssemblies != null) removedAssemblies.Add(entry.ScriptAssembly); foreach (string includer in entry.Includers) { if (cache.TryGetValue(includer, out entry)) RemoveCachedEntryNoLock(includer, entry, removedAssemblies); } } }
private bool CheckEntryFileTimeInternal(string/*!*/ ns, CacheEntry entry) { cacheLock.EnterWriteLock(); try { return CheckEntryFileTimeNoLock(ns, entry); } finally { cacheLock.ExitWriteLock(); } }
internal void UpdateCacheEntry(CacheEntry cacheEntry) { lock (this) { UsageEntryRef usageEntryRef = cacheEntry.UsageEntryRef; if (!usageEntryRef.IsInvalid) { UsageEntry[] entryArray = this._pages[usageEntryRef.PageIndex]._entries; int index = usageEntryRef.Ref1Index; UsageEntryRef ref3 = new UsageEntryRef(usageEntryRef.PageIndex, -usageEntryRef.Ref1Index); UsageEntryRef ref4 = entryArray[index]._ref2._prev; UsageEntryRef ref5 = entryArray[index]._ref2._next; if (ref4.IsRef1) { this._pages[ref4.PageIndex]._entries[ref4.Ref1Index]._ref1._next = ref5; } else if (ref4.IsRef2) { this._pages[ref4.PageIndex]._entries[ref4.Ref2Index]._ref2._next = ref5; } else { this._lastRefHead = ref5; } if (ref5.IsRef1) { this._pages[ref5.PageIndex]._entries[ref5.Ref1Index]._ref1._prev = ref4; } else if (ref5.IsRef2) { this._pages[ref5.PageIndex]._entries[ref5.Ref2Index]._ref2._prev = ref4; } else { this._lastRefTail = ref4; } if (this._addRef2Head == ref3) { this._addRef2Head = ref5; } entryArray[index]._ref2 = entryArray[index]._ref1; ref4 = entryArray[index]._ref2._prev; ref5 = entryArray[index]._ref2._next; if (ref4.IsRef1) { this._pages[ref4.PageIndex]._entries[ref4.Ref1Index]._ref1._next = ref3; } else if (ref4.IsRef2) { this._pages[ref4.PageIndex]._entries[ref4.Ref2Index]._ref2._next = ref3; } else { this._lastRefHead = ref3; } if (ref5.IsRef1) { this._pages[ref5.PageIndex]._entries[ref5.Ref1Index]._ref1._prev = ref3; } else if (ref5.IsRef2) { this._pages[ref5.PageIndex]._entries[ref5.Ref2Index]._ref2._prev = ref3; } else { this._lastRefTail = ref3; } entryArray[index]._ref1._prev = UsageEntryRef.INVALID; entryArray[index]._ref1._next = this._lastRefHead; if (this._lastRefHead.IsRef1) { this._pages[this._lastRefHead.PageIndex]._entries[this._lastRefHead.Ref1Index]._ref1._prev = usageEntryRef; } else if (this._lastRefHead.IsRef2) { this._pages[this._lastRefHead.PageIndex]._entries[this._lastRefHead.Ref2Index]._ref2._prev = usageEntryRef; } else { this._lastRefTail = usageEntryRef; } this._lastRefHead = usageEntryRef; } } }
private void SetCacheEntryNoLock(string/*!*/ ns, CacheEntry entry, bool setIncludees, bool setIncluders) { // new entry -> includers need to know about it: if (setIncludees && !cache.ContainsKey(ns)) { foreach (string includer in entry.Includers) { CacheEntry incl_entry; if (cache.TryGetValue(includer, out incl_entry)) incl_entry.Includees.Add(ns); } } if (setIncluders) { foreach (string includee in entry.Includees) { CacheEntry incl_entry; if (cache.TryGetValue(includee, out incl_entry)) incl_entry.Includers.Add(ns); } } cache[ns] = entry; }
internal void UpdateCacheEntry(CacheEntry cacheEntry) { lock (this) { int index = cacheEntry.UsageIndex; if (index < 0) { return; } Debug.Assert(index > 0); Debug.Assert(cacheEntry == _entries[index].cacheEntry, "cacheEntry == _entries[index].cacheEntry"); // remove ref2 from list int prev = _entries[index].ref2.prev; int next = _entries[index].ref2.next; if (prev >= 0) { Debug.Assert(_entries[prev].ref1.next == -index, "_entries[prev].ref1.next == -index"); _entries[prev].ref1.next = next; } else { Debug.Assert(_entries[-prev].ref2.next == -index, "_entries[-prev].ref2.next == -index"); _entries[-prev].ref2.next = next; } if (next >= 0) { Debug.Assert(_entries[next].ref1.prev == -index, "_entries[next].ref1.prev == -index"); _entries[next].ref1.prev = prev; } else { Debug.Assert(_entries[-next].ref2.prev == -index, "_entries[-next].ref2.prev == -index"); _entries[-next].ref2.prev = prev; } // move ref1 to ref2 _entries[index].ref2 = _entries[index].ref1; prev = _entries[index].ref2.prev; next = _entries[index].ref2.next; if (prev >= 0) { Debug.Assert(_entries[prev].ref1.next == index, "_entries[prev].ref1.next == index"); _entries[prev].ref1.next = -index; } else { Debug.Assert(_entries[-prev].ref2.next == index, "_entries[-prev].ref2.next == index"); _entries[-prev].ref2.next = -index; } if (next >= 0) { Debug.Assert(_entries[next].ref1.prev == index, "_entries[next].ref1.prev == index"); _entries[next].ref1.prev = -index; } else { Debug.Assert(_entries[-next].ref2.prev == index, "_entries[-next].ref2.prev == index"); _entries[-next].ref2.prev = -index; } // put ref1 at head of list int firstRef = _entries[0].ref1.next; Debug.Assert(firstRef >= 0 || firstRef == -index, "firstRef >= 0 || firstRef == -index"); _entries[index].ref1.prev = 0; _entries[index].ref1.next = firstRef; _entries[0].ref1.next = index; if (firstRef >= 0) { _entries[firstRef].ref1.prev = index; } else { _entries[index].ref2.prev = index; } Debug.Trace("CacheUsageUpdate", "Updated item=" + cacheEntry.Key + ",_bucket=" + _bucket + ",_index=" + index); Debug.Validate("CacheValidateUsage", this); Debug.Dump("CacheUsageUpdate", this); } }
internal int FlushUnderUsedItems(int maxFlush) { if (_cInUse == 0) { return(0); } Debug.Assert(maxFlush > 0, "maxFlush is not greater than 0, instead is " + maxFlush); int flushed = 0; ArrayList entriesToRemove = new ArrayList(); DateTime utcNow = DateTime.UtcNow; int prev, prevNext; DateTime utcDate; int index; lock (this) { // walk the list backwards, removing items for (prev = _entries[0].ref1.prev; prev != 0 && flushed < maxFlush && _cInUse > 0; prev = prevNext) { // set prevNext before possibly freeing an item prevNext = _entries[-prev].ref2.prev; while (prevNext > 0) { Debug.Assert(prevNext != 0, "prevNext != 0"); prevNext = _entries[prevNext].ref1.prev; } // look only at ref2 items Debug.Assert(prev < 0, "prev < 0"); index = -prev; utcDate = _entries[index].utcDate; Debug.Assert(utcDate != DateTime.MinValue, "utcDate != DateTime.MinValue"); if (utcNow - utcDate > CacheUsage.NEWADD_INTERVAL) { CacheEntry cacheEntry = _entries[index].cacheEntry; Debug.Trace("CacheUsageFlushUnderUsedItem", "Flushing underused items, item=" + cacheEntry.Key + ", bucket=" + _bucket); entriesToRemove.Add(cacheEntry); flushed++; } Debug.Assert(-_entries.Length < prevNext && prevNext < _entries.Length, "-_entries.Length < prevNext && prevNext < _entries.Length"); } Debug.Validate("CacheValidateUsage", this); Debug.Dump("CacheUsageFlush", this); } CacheInternal cacheInternal = _cacheUsage.CacheInternal; foreach (CacheEntry cacheEntry in entriesToRemove) { cacheInternal.Remove(cacheEntry, CacheItemRemovedReason.Underused); } Debug.Trace("CacheUsageFlushUnderUsedTotal", "Removed " + entriesToRemove.Count + " underused items; Time=" + DateTime.UtcNow.ToLocalTime()); return(flushed); }
void RemoveEntryAtIndex(CacheEntry cacheEntry, int index) { int prev, next; int length = _entries.Length; int lengthdiv2 = length / 2; Debug.Assert(cacheEntry == _entries[index].cacheEntry, "cacheEntry == _entries[index].cacheEntry"); Debug.Assert((lengthdiv2 & 0x1) == 0, "(lengthdiv2 & 0x1) == 0"); Debug.Assert(index > 0); // update the cache entry cacheEntry.UsageIndex = -1; // update fields Debug.Assert(_entries[index].utcDate != DateTime.MinValue, "_entries[index].utcDate != DateTime.MinValue"); _cInUse--; _entries[index].utcDate = DateTime.MinValue; _entries[index].cacheEntry = null; // remove ref1 from list prev = _entries[index].ref1.prev; next = _entries[index].ref1.next; if (prev >= 0) { Debug.Assert(_entries[prev].ref1.next == index, "_entries[prev].ref1.next == index"); _entries[prev].ref1.next = next; } else { Debug.Assert(_entries[-prev].ref2.next == index, "_entries[-prev].ref2.next == index"); _entries[-prev].ref2.next = next; } if (next >= 0) { Debug.Assert(_entries[next].ref1.prev == index, "_entries[next].ref1.prev == index"); _entries[next].ref1.prev = prev; } else { Debug.Assert(_entries[-next].ref2.prev == index, "_entries[-next].ref2.prev"); _entries[-next].ref2.prev = prev; } // remove ref2 from list prev = _entries[index].ref2.prev; next = _entries[index].ref2.next; if (prev >= 0) { Debug.Assert(_entries[prev].ref1.next == -index, "_entries[prev].ref1.next == -index"); _entries[prev].ref1.next = next; } else { Debug.Assert(_entries[-prev].ref2.next == -index, "_entries[-prev].ref2.next == -index"); _entries[-prev].ref2.next = next; } if (next >= 0) { Debug.Assert(_entries[next].ref1.prev == -index, "_entries[next].ref1.prev == -index"); _entries[next].ref1.prev = prev; } else { Debug.Assert(_entries[-next].ref2.prev == -index, "_entries[-next].ref2.prev == -index"); _entries[-next].ref2.prev = prev; } Debug.Assert(_freeHead == -1 || (_entries[_freeHead].cacheEntry == null && _entries[_freeHead].utcDate == DateTime.MinValue), "_freeHead == -1 || (_entries[_freeHead].cacheEntry == null && _entries[_freeHead].utcDate == DateTime.MinValue)"); Debug.Assert(_freeTail == -1 || (_entries[_freeTail].cacheEntry == null && _entries[_freeTail].utcDate == DateTime.MinValue), "_freeTail == -1 || (_entries[_freeTail].cacheEntry == null && _entries[_freeTail].utcDate == DateTime.MinValue)"); Debug.Assert(_entries[index].cacheEntry == null && _entries[index].utcDate == DateTime.MinValue, "_entries[index].cacheEntry == null && _entries[index].utcDate == DateTime.MinValue"); // add index to free list // if entry is in first half, add to head, else add to tail if (_freeHead == -1) { Debug.Assert(_freeTail == -1, "_freeTail == -1"); _freeHead = _freeTail = index; _entries[index].ref1.next = -1; } else if (index < lengthdiv2) { Debug.Assert(_freeTail != -1, "_freeTail != -1"); _entries[index].ref1.next = _freeHead; _freeHead = index; } else { Debug.Assert(_freeTail != -1, "_freeTail != -1"); Debug.Assert(_entries[_freeTail].ref1.next == -1, "_entries[_freeTail].ref1.next == -1"); _entries[_freeTail].ref1.next = index; _entries[index].ref1.next = -1; _freeTail = index; } _cFree++; if (index >= lengthdiv2) { _cFreeLast++; } // check whether we should realloc _entries // only realloc if 2nd half is empty Debug.Assert(_cFreeLast <= lengthdiv2); if (_cFreeLast == lengthdiv2 && _cInUse * 2 <= lengthdiv2 && MIN_ENTRIES <= lengthdiv2) { int newSize = lengthdiv2; Debug.Assert(_freeHead >= 0 && _freeHead < newSize, "_freeHead >= 0 && _freeHead < newSize"); // alloc and copy to new array UsageEntry[] entries = new UsageEntry[newSize]; _freeHead = -1; _freeTail = -1; _cFree = 0; // copy the two halves, and count _cFreeLast during the second half // don't count entry at index 0 as free entries[0] = _entries[0]; int i = 1, c = 0; for (int halves = 1; halves <= 2; halves++) { _cFreeLast = 0; c += newSize / 2; for (; i < c; i++) { entries[i] = _entries[i]; if (entries[i].utcDate == DateTime.MinValue) { _cFree++; _cFreeLast++; // update free list if (_freeHead == -1) { _freeHead = i; } else { entries[_freeTail].ref1.next = i; } _freeTail = i; } } } Debug.Assert(_freeHead != -1, "_freeHead should not be -1"); Debug.Assert(_freeTail != -1, "_freeTail should not be -1"); // terminate free list entries[_freeTail].ref1.next = -1; //#if DBG // for (; i < length; i++) { // Debug.Assert(_entries[i].utcDate == 0, "_entries[i].utcDate == 0"); // } //#endif _entries = entries; Debug.Trace("CacheUsageContract", "Contracted from " + length + " to newSize"); } }
internal void AddCacheEntry(CacheEntry cacheEntry) { lock (this) { if (cacheEntry.State != CacheEntry.EntryState.AddingToCache) { return; } // reserve room for additions if (_freeHead == -1) { ExpandEntries(); } Debug.Assert(_freeHead != -1, "_freeHead != -1"); Debug.Assert(_freeTail != -1, "_freeTail != -1"); // add index int index = _freeHead; _freeHead = _entries[index].ref1.next; #if DBG Debug.Assert(_entries[index].utcDate == DateTime.MinValue, "_entries[index].utcDate == DateTime.MinValue"); Debug.Assert(_entries[index].cacheEntry == null, "_entries[index].cacheEntry == null"); Debug.Assert(_freeHead != index, "_freeHead != index"); if (_freeHead != -1) { Debug.Assert(_entries[_freeHead].utcDate == DateTime.MinValue, "_entries[_freeHead].utcDate == DateTime.MinValue"); Debug.Assert(_entries[_freeHead].cacheEntry == null, "_entries[_freeHead].cacheEntry == null"); } #endif if (_freeHead == -1) { _freeTail = -1; } _cFree--; if (index >= _entries.Length / 2) { _cFreeLast--; } // update the cache entry. cacheEntry.UsageIndex = index; // initialize index _entries[index].cacheEntry = cacheEntry; _entries[index].utcDate = DateTime.UtcNow; // add to end of list int lastRef = _entries[0].ref1.prev; Debug.Assert(lastRef <= 0, "lastRef <= 0"); _entries[index].ref1.prev = lastRef; _entries[index].ref1.next = -index; _entries[index].ref2.prev = index; _entries[index].ref2.next = 0; if (lastRef == 0) { _entries[0].ref1.next = index; } else { _entries[-lastRef].ref2.next = index; } _entries[0].ref1.prev = -index; _cInUse++; Debug.Trace("CacheUsageAdd", "Added item=" + cacheEntry.Key + ",_bucket=" + _bucket + ",_index=" + index); Debug.Validate("CacheValidateUsage", this); Debug.Dump("CacheUsageAdd", this); } }
void InitUniqueID() { StringBuilder sb = null; object l_depFileInfos, l_entries; #if !FEATURE_PAL // no File Change Monitoring // get unique id from files l_depFileInfos = _depFileInfos; if (l_depFileInfos != null) { DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { sb = new StringBuilder(); AppendFileUniqueId(oneDepFileInfo, sb); } else { DepFileInfo[] depFileInfos = (DepFileInfo[])l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null if (depFileInfo._filename != null) { if (sb == null) { sb = new StringBuilder(); } AppendFileUniqueId(depFileInfo, sb); } } } } #endif // !FEATURE_PAL // get unique id from cache entries l_entries = _entries; if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { if (sb == null) { sb = new StringBuilder(); } sb.Append(oneEntry.Key); sb.Append(oneEntry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } else { CacheEntry[] entries = (CacheEntry[])l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { if (sb == null) { sb = new StringBuilder(); } sb.Append(entry.Key); sb.Append(entry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } } } } if (sb != null) { _uniqueID = sb.ToString(); } #if DBG _isUniqueIDInitialized = true; #endif }
void DisposeOurself() { // guarantee that we execute only once if an exception // is thrown from this function by nulling fields before // we access them object l_depFileInfos = _depFileInfos; object l_entries = _entries; _objNotify = null; _depFileInfos = null; _entries = null; // stop monitoring files if (l_depFileInfos != null) { FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { fmon.StopMonitoringPath(oneDepFileInfo._filename, this); } else { DepFileInfo[] depFileInfos = (DepFileInfo[])l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null string filename = depFileInfo._filename; if (filename != null) { fmon.StopMonitoringPath(filename, this); } } } } // stop monitoring cache items if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { oneEntry.RemoveCacheDependencyNotify(this); } else { CacheEntry[] entries = (CacheEntry[])l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { entry.RemoveCacheDependencyNotify(this); } } } } #if USE_MEMORY_CACHE if (_fileChangeMonitor != null) { _fileChangeMonitor.Dispose(); } if (_entryChangeMonitor != null) { _entryChangeMonitor.Dispose(); } #endif }
internal void AddCacheEntry(CacheEntry cacheEntry) { lock (this) { if (this._freeEntryList._head == -1) { this.Expand(); } UsageEntryRef freeUsageEntry = this.GetFreeUsageEntry(); UsageEntryRef ref3 = new UsageEntryRef(freeUsageEntry.PageIndex, -freeUsageEntry.Ref1Index); cacheEntry.UsageEntryRef = freeUsageEntry; UsageEntry[] entryArray = this._pages[freeUsageEntry.PageIndex]._entries; int index = freeUsageEntry.Ref1Index; entryArray[index]._cacheEntry = cacheEntry; entryArray[index]._utcDate = DateTime.UtcNow; entryArray[index]._ref1._prev = UsageEntryRef.INVALID; entryArray[index]._ref2._next = this._addRef2Head; if (this._lastRefHead.IsInvalid) { entryArray[index]._ref1._next = ref3; entryArray[index]._ref2._prev = freeUsageEntry; this._lastRefTail = ref3; } else { UsageEntryRef iNVALID; UsageEntryRef ref5; entryArray[index]._ref1._next = this._lastRefHead; if (this._lastRefHead.IsRef1) { this._pages[this._lastRefHead.PageIndex]._entries[this._lastRefHead.Ref1Index]._ref1._prev = freeUsageEntry; } else if (this._lastRefHead.IsRef2) { this._pages[this._lastRefHead.PageIndex]._entries[this._lastRefHead.Ref2Index]._ref2._prev = freeUsageEntry; } else { this._lastRefTail = freeUsageEntry; } if (this._addRef2Head.IsInvalid) { ref5 = this._lastRefTail; iNVALID = UsageEntryRef.INVALID; } else { ref5 = this._pages[this._addRef2Head.PageIndex]._entries[this._addRef2Head.Ref2Index]._ref2._prev; iNVALID = this._addRef2Head; } entryArray[index]._ref2._prev = ref5; if (ref5.IsRef1) { this._pages[ref5.PageIndex]._entries[ref5.Ref1Index]._ref1._next = ref3; } else if (ref5.IsRef2) { this._pages[ref5.PageIndex]._entries[ref5.Ref2Index]._ref2._next = ref3; } else { this._lastRefHead = ref3; } if (iNVALID.IsRef1) { this._pages[iNVALID.PageIndex]._entries[iNVALID.Ref1Index]._ref1._prev = ref3; } else if (iNVALID.IsRef2) { this._pages[iNVALID.PageIndex]._entries[iNVALID.Ref2Index]._ref2._prev = ref3; } else { this._lastRefTail = ref3; } } this._lastRefHead = freeUsageEntry; this._addRef2Head = ref3; this._cEntriesInUse++; } }
/// <summary> /// Store entry in the cache /// </summary> /// <param name="ns">Key (namespace)</param> /// <param name="entry">Value (entry)</param> /// <param name="setIncludees">Set this file as included in every includer</param> /// <param name="setIncluders">Set this file as includer for every included script</param> private void SetCacheEntry(string/*!*/ ns, CacheEntry entry, bool setIncludees, bool setIncluders) { cacheLock.EnterWriteLock(); try { SetCacheEntryNoLock(ns, entry, setIncludees, setIncluders); } finally { cacheLock.ExitWriteLock(); } }
private CacheItemPolicy GetPolicy(CacheEntry newEntry) { CacheItemPolicy policy = new CacheItemPolicy(); policy.SlidingExpiration = newEntry.SlidingExpiration; if (policy.SlidingExpiration == ObjectCache.NoSlidingExpiration) { policy.AbsoluteExpiration = (newEntry.UtcExpires != Cache.NoAbsoluteExpiration) ? newEntry.UtcExpires : ObjectCache.InfiniteAbsoluteExpiration; } if (newEntry.Dependency != null) { policy.ChangeMonitors.Add(new DependencyChangeMonitor(newEntry.Dependency)); } policy.Priority = (newEntry.UsageBucket == 0xff) ? System.Runtime.Caching.CacheItemPriority.NotRemovable : System.Runtime.Caching.CacheItemPriority.Default; CacheItemRemovedCallback callback = newEntry.CacheItemRemovedCallback; if (callback != null) { policy.RemovedCallback = (new RemovedCallback(callback)).CacheEntryRemovedCallback; } return policy; }
private List<ScriptAssembly>/*!*/ RemoveCachedEntry(string/*!*/ ns, CacheEntry entry) { List<ScriptAssembly> removed_assemblies = new List<ScriptAssembly>(); cacheLock.EnterWriteLock(); try { RemoveCachedEntryNoLock(ns, entry, removed_assemblies); } finally { cacheLock.ExitWriteLock(); } return removed_assemblies; }
internal override CacheEntry UpdateCache(CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld) { valueOld = null; CacheEntry entry = null; string key = cacheKey.Key; bool isPublic = cacheKey.IsPublic; if (_disposed) { return null; } MemoryCache cache = (isPublic) ? _cachePublic : _cacheInternal; if (newEntry == null && !replace) { // get object o = cache.Get(key); if (o != null) { entry = new CacheEntry(key, o, null, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, isPublic); entry.State = CacheEntry.EntryState.AddedToCache; } } else if (newEntry != null && replace) { // set try { } finally { // prevent ThreadAbortEx from interrupting these calls CacheItemPolicy policy = GetPolicy(newEntry); cache.Set(key, newEntry.Value, policy); } } else if (newEntry != null && !replace) { // add try { } finally { // prevent ThreadAbortEx from interrupting these calls CacheItemPolicy policy = GetPolicy(newEntry); Object o = cache.AddOrGetExisting(key, newEntry.Value, policy); if (o != null) { entry = new CacheEntry(key, o, null, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, isPublic); entry.State = CacheEntry.EntryState.AddedToCache; } } } else { // remove valueOld = cache.Remove(key); } return entry; }
/// <summary> /// Checks whether timestamp of the file in the cache matches timestamp /// loded from the compiled assembly - this prevents us from using compiled /// assembly when the source file was modified (when the watcher was not running) /// </summary> private bool CheckEntryFileTime(string/*!*/ ns, CacheEntry entry) { if (entry.FileTimeChecked) return true; return CheckEntryFileTimeInternal(ns, entry); }
internal void Remove(CacheEntry cacheEntry) { byte expiresBucket = cacheEntry.ExpiresBucket; if (expiresBucket != 0xff) { this._buckets[expiresBucket].RemoveCacheEntry(cacheEntry); } }
private bool CheckEntryFileTimeNoLock(string/*!*/ ns, CacheEntry entry) { if (entry.FileTimeChecked) return true; try { FullPath source_path = ScriptModule.GetPathFromSubnamespace(ns).ToFullPath(Configuration.Application.Compiler.SourceRoot); entry.FileTimeChecked = true; // is compilation obsolete? if (entry.BuildTimestamp < File.GetLastWriteTime(source_path)) { RemoveCachedEntryNoLock(ns, entry, null); return false; } } catch { RemoveCachedEntryNoLock(ns, entry, null); return false; } // (When checking file first time after application starts) // Make sure that all included files (Includees) are up to date foreach (string includee in entry.Includees) { if (cache.TryGetValue(includee, out entry) && !CheckEntryFileTimeNoLock(includee, entry)) { RemoveCachedEntryNoLock(ns, entry, null); return false; } } return true; }
internal void UtcUpdate(CacheEntry cacheEntry, DateTime utcNewExpires) { int expiresBucket = cacheEntry.ExpiresBucket; int index = this.UtcCalcExpiresBucket(utcNewExpires); if (expiresBucket != index) { if (expiresBucket != 0xff) { this._buckets[expiresBucket].RemoveCacheEntry(cacheEntry); cacheEntry.UtcExpires = utcNewExpires; this._buckets[index].AddCacheEntry(cacheEntry); } } else if (expiresBucket != 0xff) { this._buckets[expiresBucket].UtcUpdateCacheEntry(cacheEntry, utcNewExpires); } }
private bool LoadSSA(string ns, string assemblyfile, out CacheEntry cache_entry) { // load assembly (ssa) Assembly assembly = Assembly.LoadFrom(assemblyfile); SingleScriptAssembly ssa = (SingleScriptAssembly)ScriptAssembly.LoadFromAssembly(applicationContext, assembly); // find type <Script> var scriptType = ssa.GetScriptType(); if (scriptType != null) { // recursively check (and load) included assemblies // (includees and includers are set for all loaded CacheEntries except the // inclusion to the currently loaded script - this is set later) Dictionary<string, CacheEntry> temporaryCache = new Dictionary<string, CacheEntry>(); if (LoadIncludeesRecursive(ns, scriptType, ssa.RealModule, false, null, temporaryCache)) { cache_entry = temporaryCache[ns]; // cached SSA is OK, reuse it foreach (KeyValuePair<string, CacheEntry> entryTmp in temporaryCache) if (entryTmp.Value != null) SetCacheEntry(entryTmp.Key, entryTmp.Value, false, false); return true; } } // otherwise cache_entry = default(CacheEntry); return false; }
internal object DoInsert( bool isPublic, string key, object value, CacheDependency dependencies, DateTime utcAbsoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback, bool replace) { /* * If we throw an exception, prevent a leak by a user who * writes the following: * * Cache.Insert(key, value, new CacheDependency(file)); */ using (dependencies) { CacheEntry entry; object dummy; entry = new CacheEntry( key, value, dependencies, onRemoveCallback, utcAbsoluteExpiration, slidingExpiration, priority, isPublic); entry = UpdateCache(entry, entry, replace, CacheItemRemovedReason.Removed, out dummy); /* * N.B. A set can fail if two or more threads set the same key * at the same time. */ #if DBG if (replace) { string yesno = (entry != null) ? "succeeded" : "failed"; Debug.Trace("CacheAPIInsert", "Cache.Insert " + yesno + ": " + key); } else { if (entry == null) { Debug.Trace("CacheAPIAdd", "Cache.Add added new item: " + key); } else { Debug.Trace("CacheAPIAdd", "Cache.Add returned existing item: " + key); } } #endif if (entry != null) { return entry.Value; } else { return null; } } }
internal object DoInsert(bool isPublic, string key, object value, CacheDependency dependencies, DateTime utcAbsoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback, bool replace) { using (dependencies) { object obj2; CacheEntry cacheKey = new CacheEntry(key, value, dependencies, onRemoveCallback, utcAbsoluteExpiration, slidingExpiration, priority, isPublic); cacheKey = this.UpdateCache(cacheKey, cacheKey, replace, CacheItemRemovedReason.Removed, out obj2); if (cacheKey != null) { return cacheKey.Value; } return null; } }
public static object GetLock(string key, TimeSpan refreshInterval, TimeSpan slidingExpiration, bool persistToFile, CacheItemPriority priority, TCache.CacheLoaderDelegate cacheLoader) { lock (_ReadLockbox) // could use Instance.SyncRoot for all access { CacheEntry entry; if (ContainsCacheEntry(key)) { entry = Instance[key]; entry.LastUse = DateTime.Now; } else { // escalate to a writer lock for this operation lock (_WriteLockbox) // could use Instance.SyncRoot for all access { entry = new CacheEntry(); entry.CacheLoader = cacheLoader; entry.RefreshInterval = refreshInterval; entry.SlidingExpiration = slidingExpiration; entry.CacheItemPriority = priority; entry.LastUse = DateTime.Now; entry.PersistToFile = persistToFile; if (persistToFile) { string filename = key; foreach (char c in Path.GetInvalidFileNameChars()) { filename = filename.Replace( c.ToString(), "^_^" ); } filename += ".bin"; if (filename.Length > 255) throw new ApplicationException("Can't use persist to key name is greater than 255."); string basePath = null; System.Web.HttpContext context = System.Web.HttpContext.Current; if (context == null) basePath = AppDomain.CurrentDomain.BaseDirectory; else basePath = context.Server.MapPath("~/"); basePath = Path.Combine(basePath, "LocalCache"); if (Directory.Exists(basePath) == false) Directory.CreateDirectory(basePath); entry.PersistedFilename = Path.Combine(basePath, filename); } Instance.Add(key, entry); } } return entry.Locker; } }
/* * 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) { valueOld = null; CacheEntry entry = null; string key = cacheKey.Key; bool isPublic = cacheKey.IsPublic; if (_disposed) { return(null); } MemoryCache cache = (isPublic) ? _cachePublic : _cacheInternal; if (newEntry == null && !replace) { // get object o = cache.Get(key); if (o != null) { entry = new CacheEntry(key, o, null, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, isPublic); entry.State = CacheEntry.EntryState.AddedToCache; } } else if (newEntry != null && replace) { // set try { } finally { // prevent ThreadAbortEx from interrupting these calls CacheItemPolicy policy = GetPolicy(newEntry); cache.Set(key, newEntry.Value, policy); } } else if (newEntry != null && !replace) { // add try { } finally { // prevent ThreadAbortEx from interrupting these calls CacheItemPolicy policy = GetPolicy(newEntry); Object o = cache.AddOrGetExisting(key, newEntry.Value, policy); if (o != null) { entry = new CacheEntry(key, o, null, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, isPublic); entry.State = CacheEntry.EntryState.AddedToCache; } } } else { // remove valueOld = cache.Remove(key); } return(entry); }
internal abstract CacheEntry UpdateCache( CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld);
void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart); return; } #endif DepFileInfo[] depFileInfos = s_depFileInfosEmpty; CacheEntry[] depEntries = s_entriesEmpty; string [] filenames, cachekeys; CacheInternal cacheInternal; _bits = new SafeBitVector32(0); // copy array argument contents so they can't be changed beneath us if (filenamesArg != null) { filenames = (string [])filenamesArg.Clone(); } else { filenames = null; } if (cachekeysArg != null) { cachekeys = (string [])cachekeysArg.Clone(); } else { cachekeys = null; } _utcLastModified = DateTime.MinValue; try { // validate filenames array if (filenames == null) { filenames = s_stringsEmpty; } else { foreach (string f in filenames) { if (f == null) { throw new ArgumentNullException("filenamesArg"); } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } if (cachekeys == null) { cachekeys = s_stringsEmpty; } else { // validate cachekeys array foreach (string k in cachekeys) { if (k == null) { throw new ArgumentNullException("cachekeysArg"); } } } // copy all parts of another dependency if provided if (dependency == null) { dependency = s_dependencyEmpty; } else { if (dependency.GetType() != s_dependencyEmpty.GetType()) { throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type)); } // Copy the parts of the dependency we need before // we reference them, as the dependency can change // underneath us. object d_depFileInfos = dependency._depFileInfos; object d_entries = dependency._entries; DateTime d_lastModified = dependency._utcLastModified; // if the dependency we're copying has changed, we're done if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // copy depFileInfos if (d_depFileInfos != null) { if (d_depFileInfos is DepFileInfo) { depFileInfos = new DepFileInfo[1] { (DepFileInfo)d_depFileInfos }; } else { depFileInfos = (DepFileInfo[])(d_depFileInfos); } // verify that the object was fully constructed // and that we have permission to discover the file foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; if (f == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } // copy cache entries if (d_entries != null) { if (d_entries is CacheEntry) { depEntries = new CacheEntry[1] { (CacheEntry)(d_entries) }; } else { depEntries = (CacheEntry[])(d_entries); // verify that the object was fully constructed foreach (CacheEntry entry in depEntries) { if (entry == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } } } } _utcLastModified = d_lastModified; } // Monitor files for changes int lenMyDepFileInfos = depFileInfos.Length + filenames.Length; if (lenMyDepFileInfos > 0) { DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos]; FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange); FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; int i; for (i = 0; i < lenMyDepFileInfos; i++) { myDepFileInfos[i] = new DepFileInfo(); } // monitor files from the existing dependency // note that we don't check for start times in the existing dependency i = 0; foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; } // monitor new files DateTime utcNow = DateTime.MinValue; foreach (string f in filenames) { DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; if (utcLastWrite > _utcLastModified) { _utcLastModified = utcLastWrite; } // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (utcNow == DateTime.MinValue) { utcNow = DateTime.UtcNow; } Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite); if (utcLastWrite >= utcStart && !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) // See VSWhidbey 400917 { Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f); _bits[CHANGED] = true; break; } } } if (myDepFileInfos.Length == 1) { _depFileInfos = myDepFileInfos[0]; } else { _depFileInfos = myDepFileInfos; } } // Monitor other cache entries for changes int lenMyEntries = depEntries.Length + cachekeys.Length; if (lenMyEntries > 0 && !_bits[CHANGED]) { CacheEntry[] myEntries = new CacheEntry[lenMyEntries]; // Monitor entries from the existing cache dependency int i = 0; foreach (CacheEntry entry in depEntries) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; } // Monitor new entries specified for this depenedency // Entries must be added to cache, and created before the startTime cacheInternal = HttpRuntime.CacheInternal; foreach (string k in cachekeys) { CacheEntry entry = (CacheEntry)cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry); if (entry != null) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; if (entry.UtcCreated > _utcLastModified) { _utcLastModified = entry.UtcCreated; } if (entry.State != CacheEntry.EntryState.AddedToCache || entry.UtcCreated > utcStart) { #if DBG if (entry.State != CacheEntry.EntryState.AddedToCache) { Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k); } else { Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k); } #endif _bits[CHANGED] = true; break; } } else { Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k); _bits[CHANGED] = true; break; } } if (myEntries.Length == 1) { _entries = myEntries[0]; } else { _entries = myEntries; } } _bits[BASE_INIT] = true; if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { DisposeInternal(); } Debug.Assert(_objNotify == null, "_objNotify == null"); } catch { // derived constructor will not execute due to the throw, // so we just force a dispose on ourselves _bits[BASE_INIT] = true; DisposeInternal(); throw; } finally { InitUniqueID(); } }
void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart); return; } #endif DepFileInfo[] depFileInfos = s_depFileInfosEmpty; CacheEntry[] depEntries = s_entriesEmpty; string [] filenames, cachekeys; CacheInternal cacheInternal; _bits = new SafeBitVector32(0); // copy array argument contents so they can't be changed beneath us if (filenamesArg != null) { filenames = (string []) filenamesArg.Clone(); } else { filenames = null; } if (cachekeysArg != null) { cachekeys = (string []) cachekeysArg.Clone(); } else { cachekeys = null; } _utcLastModified = DateTime.MinValue; try { // validate filenames array if (filenames == null) { filenames = s_stringsEmpty; } else { foreach (string f in filenames) { if (f == null) { throw new ArgumentNullException("filenamesArg"); } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } if (cachekeys == null) { cachekeys = s_stringsEmpty; } else { // validate cachekeys array foreach (string k in cachekeys) { if (k == null) { throw new ArgumentNullException("cachekeysArg"); } } } // copy all parts of another dependency if provided if (dependency == null) { dependency = s_dependencyEmpty; } else { if (dependency.GetType() != s_dependencyEmpty.GetType()) { throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type)); } // Copy the parts of the dependency we need before // we reference them, as the dependency can change // underneath us. object d_depFileInfos = dependency._depFileInfos; object d_entries = dependency._entries; DateTime d_lastModified = dependency._utcLastModified; // if the dependency we're copying has changed, we're done if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // copy depFileInfos if (d_depFileInfos != null) { if (d_depFileInfos is DepFileInfo) { depFileInfos = new DepFileInfo[1] {(DepFileInfo) d_depFileInfos}; } else { depFileInfos = (DepFileInfo[]) (d_depFileInfos); } // verify that the object was fully constructed // and that we have permission to discover the file foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; if (f == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } // copy cache entries if (d_entries != null) { if (d_entries is CacheEntry) { depEntries = new CacheEntry[1] {(CacheEntry) (d_entries)}; } else { depEntries = (CacheEntry[]) (d_entries); // verify that the object was fully constructed foreach (CacheEntry entry in depEntries) { if (entry == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } } } } _utcLastModified = d_lastModified; } // Monitor files for changes int lenMyDepFileInfos = depFileInfos.Length + filenames.Length; if (lenMyDepFileInfos > 0) { DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos]; FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange); FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; int i; for (i = 0; i < lenMyDepFileInfos; i++) { myDepFileInfos[i] = new DepFileInfo(); } // monitor files from the existing dependency // note that we don't check for start times in the existing dependency i = 0; foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; } // monitor new files DateTime utcNow = DateTime.MinValue; foreach (string f in filenames) { DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; if (utcLastWrite > _utcLastModified) { _utcLastModified = utcLastWrite; } // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (utcNow == DateTime.MinValue) { utcNow = DateTime.UtcNow; } Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite); if (utcLastWrite >= utcStart && !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917 Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f); _bits[CHANGED] = true; break; } } } if (myDepFileInfos.Length == 1) { _depFileInfos = myDepFileInfos[0]; } else { _depFileInfos = myDepFileInfos; } } // Monitor other cache entries for changes int lenMyEntries = depEntries.Length + cachekeys.Length; if (lenMyEntries > 0 && !_bits[CHANGED]) { CacheEntry[] myEntries = new CacheEntry[lenMyEntries]; // Monitor entries from the existing cache dependency int i = 0; foreach (CacheEntry entry in depEntries) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; } // Monitor new entries specified for this depenedency // Entries must be added to cache, and created before the startTime cacheInternal = HttpRuntime.CacheInternal; foreach (string k in cachekeys) { CacheEntry entry = (CacheEntry) cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry); if (entry != null) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; if (entry.UtcCreated > _utcLastModified) { _utcLastModified = entry.UtcCreated; } if ( entry.State != CacheEntry.EntryState.AddedToCache || entry.UtcCreated > utcStart) { #if DBG if (entry.State != CacheEntry.EntryState.AddedToCache) { Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k); } else { Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k); } #endif _bits[CHANGED] = true; break; } } else { Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k); _bits[CHANGED] = true; break; } } if (myEntries.Length == 1) { _entries = myEntries[0]; } else { _entries = myEntries; } } _bits[BASE_INIT] = true; if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { DisposeInternal(); } Debug.Assert(_objNotify == null, "_objNotify == null"); } catch { // derived constructor will not execute due to the throw, // so we just force a dispose on ourselves _bits[BASE_INIT] = true; DisposeInternal(); throw; } finally { InitUniqueID(); } }
/// <summary> /// Loads script from cache (in-memory tables) or from /// previously compiled dll in ASP.NET Temporary files. /// </summary> private bool TryLoadCachedEntry(string/*!*/ ns, PhpSourceFile/*!*/ sourceFile, out CacheEntry cache_entry) { // first try in-memory cache if (TryGetCachedEntry(ns, out cache_entry)) { Debug.WriteLine("WSSM", "Cache hit."); if (CheckEntryFileTime(ns, cache_entry) || !Configuration.Application.Compiler.WatchSourceChanges) return true; } lock(tempLoadLocks[unchecked((uint)ns.GetHashCode()) % tempLoadLocks.Length]) { // double checked lock if (TryGetCachedEntry(ns, out cache_entry)) return true; // do not check source time, since it was currently created // try to find previously compiled assembly in temporary files if (TryLoadTemporaryCompiledNoLock(ns, sourceFile, out cache_entry)) { Debug.WriteLine("WSSM", "Loaded from Temporary files."); return true; } } return false; }
internal override CacheEntry UpdateCache(CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld) { int hashCode = cacheKey.Key.GetHashCode(); return this.GetCacheSingle(hashCode).UpdateCache(cacheKey, newEntry, replace, removedReason, out valueOld); }
/// <summary> /// Get script for specified namespace from in-memory cache /// </summary> private bool TryGetCachedEntry(string/*!*/ ns, out CacheEntry entry) { cacheLock.EnterReadLock(); try { return cache.TryGetValue(ns, out entry); } finally { cacheLock.ExitReadLock(); } }