/// <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);
 }
Example #6
0
        /*
         * 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);
        }
Example #7
0
        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 &lt;Script&gt; 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();
     }
 }
Example #12
0
 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;
		}
Example #14
0
        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);
            }
        }
Example #15
0
        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);
        }
Example #16
0
        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");
            }
        }
Example #17
0
        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);
            }
        }
Example #18
0
        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
        }
Example #19
0
        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
        }
Example #20
0
 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();
			}
		}
Example #22
0
 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;
		}
Example #24
0
        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;
        }
Example #30
0
        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;
     }
 }
Example #32
0
        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;
            }
        }
Example #33
0
        /*
         * 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;
        }
Example #34
0
        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);
        }
Example #35
0
 internal abstract CacheEntry UpdateCache(
         CacheKey                cacheKey,
         CacheEntry              newEntry,
         bool                    replace,
         CacheItemRemovedReason  removedReason,
         out object              valueOld);
Example #36
0
        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();
			}
		}