cache.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / Cache / cache.cs / 1601785 / cache.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 * Cache class 
 *
 * Copyright (c) 1999 Microsoft Corporation 
 */

namespace System.Web.Caching {
    using System.Collections; 
    using System.Collections.Specialized;
    using System.Configuration; 
    using System.Diagnostics; 
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime.InteropServices; 
    using System.Threading;
    using System.Web.Util;
    using System.Web;
    using Microsoft.Win32; 
    using System.Security.Permissions;
    using System.Globalization; 
    using System.Web.Configuration; 
    using System.Web.Hosting;
    using System.Web.Management; 
    using Debug = System.Web.Util.Debug;


    ///  
    /// Represents the method that will handle the 
    /// event of a System.Web.Caching.Cache instance. 
    ///  
    public delegate void CacheItemRemovedCallback(
            string key, object value, CacheItemRemovedReason reason); 

    /// 
    /// Represents the method that will handle the 
    /// event of a System.Web.Caching.Cache instance. 
    /// 
    [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", 
                     Justification="Shipped this way in NetFx 2.0 SP2")] 
    public delegate void CacheItemUpdateCallback(
            string key, CacheItemUpdateReason reason, 
            out object expensiveObject, out CacheDependency dependency, out DateTime absoluteExpiration, out TimeSpan slidingExpiration);

    /// 
    ///  Specifies the relative priority of items stored in the System.Web.Caching.Cache. When the Web 
    ///    server runs low on memory, the Cache selectively purges items to free system
    ///    memory. Items with higher priorities are less likely to be removed from the 
    ///    cache when the server is under load. Web 
    ///    applications can use these
    ///    values to prioritize cached items relative to one another. The default is 
    ///    normal.
    /// 
    public enum CacheItemPriority {
 
        /// 
        ///     The cahce items with this priority level will be the first 
        ///       to be removed when the server frees system memory by deleting items from the 
        ///       cache.
        ///  
        Low = 1,

        /// 
        ///     The cache items with this priority level 
        ///       are in the second group to be removed when the server frees system memory by
        ///       deleting items from the cache.  
        ///  
        BelowNormal,
 
        /// 
        ///     The cache items with this priority level are in
        ///       the third group to be removed when the server frees system memory by deleting items from the cache. This is the default. 
        ///  
        Normal,
 
        ///  
        ///     The cache items with this priority level are in the
        ///       fourth group to be removed when the server frees system memory by deleting items from the 
        ///       cache. 
        /// 
        AboveNormal,
 
        /// 
        ///    The cache items with this priority level are in the fifth group to be removed 
        ///       when the server frees system memory by deleting items from the cache.  
        /// 
        High, 

        /// 
        ///    The cache items with this priority level will not be removed when the server
        ///       frees system memory by deleting items from the cache.  
        /// 
        NotRemovable, 
 
        /// 
        ///    The default value is Normal. 
        /// 
        Default = Normal
    }
 

    ///  
    ///    Specifies the reason that a cached item was removed. 
    /// 
    public enum CacheItemRemovedReason { 

        /// 
        /// The item was removed from the cache by the 'System.Web.Caching.Cache.Remove' method, or by an System.Web.Caching.Cache.Insert method call specifying the same key.
        ///  
        Removed = 1,
 
        ///  
        ///    The item was removed from the cache because it expired. 
        ///  
        Expired,

        /// 
        ///    The item was removed from the cache because the value in the hitInterval 
        ///       parameter was not met, or because the system removed it to free memory.
        ///  
        Underused, 

        ///  
        ///    The item was removed from the cache because a file or key dependency was
        ///       changed.
        /// 
        DependencyChanged 
    }
 
    ///  
    ///    Specifies the reason why a cached item needs to be updated.
    ///  
    [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue",
            Justification = "This enum should mirror CacheItemRemovedReason enum in design")]
    public enum CacheItemUpdateReason {
 
        /// 
        ///    The item needs to be updated because it expired.  
        ///  
        Expired = 1,
 
        /// 
        ///    The item needs to be updated because a file or key dependency was
        ///       changed.
        ///  
        DependencyChanged
    } 
 
    enum CacheGetOptions {
        None                = 0, 
        ReturnCacheEntry    = 0x1,
    }

 
    /// 
    ///    Implements the cache for a Web application. There is only one instance of 
    ///       this class per application domain, and it remains valid only as long as the 
    ///       application domain remains active. Information about an instance of this class
    ///       is available through the  property of the System.Web.HttpContext. 
    /// 

    //
    // Extra notes: 
    // - The Cache object contains a CacheInternal object.
    // - The CacheInternal object is either a CacheSingle, or a CacheMultiple which contains mulitple 
    //  CacheSingle objects. 
    //
    public sealed class Cache : IEnumerable { 

        /// 
        ///    Sets the absolute expiration policy to, in essence,
        ///       never. When set, this field is equal to the the System.DateTime.MaxValue , which is a constant 
        ///       representing the largest possible  value. The maximum date and
        ///       time value is equivilant to "12/31/9999 11:59:59 PM". This field is read-only. 
        ///  
        public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
 

        /// 
        ///    Sets the amount of time for sliding cache expirations to
        ///       zero. When set, this field is equal to the System.TimeSpan.Zero field, which is a constant value of 
        ///       zero. This field is read-only.
        ///  
        public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero; 

        CacheInternal   _cacheInternal; 
        static CacheItemRemovedCallback s_sentinelRemovedCallback = new CacheItemRemovedCallback(SentinelEntry.OnCacheItemRemovedCallback);

        /// 
        ///  
        ///    This constructor is for internal use only, and was accidentally made public - do not use.
        ///  
        [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] 
        public Cache() {
        } 

        //
        // internal ctor used by CacheCommon that avoids the demand for UnmanagedCode.
        // 
        internal Cache(int dummy) {
        } 
 
        internal void SetCacheInternal(CacheInternal cacheInternal) {
            _cacheInternal = cacheInternal; 
        }


        ///  
        ///    Gets the number of items stored in the cache. This value can be useful when
        ///       monitoring your application's performance or when using the ASP.NET tracing 
        ///       functionality. 
        /// 
        public int Count { 
            get {
                return _cacheInternal.PublicCount;
            }
        } 

 
        ///  
        IEnumerator IEnumerable.GetEnumerator() {
            return ((IEnumerable)_cacheInternal).GetEnumerator(); 
        }


        ///  
        ///    Returns a dictionary enumerator used for iterating through the key/value
        ///       pairs contained in the cache. Items can be added to or removed from the cache 
        ///       while this method is enumerating through the cache items. 
        /// 
        public IDictionaryEnumerator GetEnumerator() { 
            return _cacheInternal.GetEnumerator();
        }

 
        /// 
        ///    Gets or sets an item in the cache. 
        ///  
        public object this[string key] {
            get { 
                return Get(key);
            }

            set { 
                Insert(key, value);
            } 
        } 

        private class SentinelEntry { 
            private string _key;
            private CacheDependency _expensiveObjectDependency;
            private CacheItemUpdateCallback _cacheItemUpdateCallback;
 
            public SentinelEntry(string key, CacheDependency expensiveObjectDependency, CacheItemUpdateCallback callback) {
                _key = key; 
                _expensiveObjectDependency = expensiveObjectDependency; 
                _cacheItemUpdateCallback = callback;
            } 

            public string Key {
                get { return _key; }
            } 

            public CacheDependency ExpensiveObjectDependency { 
                get { return _expensiveObjectDependency; } 
            }
 
            public CacheItemUpdateCallback CacheItemUpdateCallback {
                get { return _cacheItemUpdateCallback; }
            }
 
            public static void OnCacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
                CacheItemUpdateReason updateReason; 
                SentinelEntry entry = value as SentinelEntry; 

                switch (reason) { 
                    case CacheItemRemovedReason.Expired:
                        updateReason = CacheItemUpdateReason.Expired;
                        break;
                    case CacheItemRemovedReason.DependencyChanged: 
                        updateReason = CacheItemUpdateReason.DependencyChanged;
                        if (entry.ExpensiveObjectDependency.HasChanged) { 
                            // If the expensiveObject has been removed explicitly by Cache.Remove, 
                            // return from the SentinelEntry removed callback
                            // thus effectively removing the SentinelEntry from the cache. 
                            return;
                        }
                        break;
                    case CacheItemRemovedReason.Underused: 
                        Debug.Fail("Reason should never be CacheItemRemovedReason.Underused since the entry was inserted as NotRemovable.");
                        return; 
                    default: 
                        // do nothing if reason is Removed
                        return; 
                }

                CacheDependency cacheDependency;
                DateTime absoluteExpiration; 
                TimeSpan slidingExpiration;
                object expensiveObject; 
                CacheItemUpdateCallback callback = entry.CacheItemUpdateCallback; 
                // invoke update callback
                try { 
                    callback(entry.Key, updateReason, out expensiveObject, out cacheDependency, out absoluteExpiration, out slidingExpiration);
                    // Dev10 861163 - Only update the "expensive" object if the user returns a new object and the
                    // cache dependency hasn't changed.  (Inserting with a cache dependency that has already changed will cause recursion.)
                    if (expensiveObject != null && (cacheDependency == null || !cacheDependency.HasChanged)) { 
                        HttpRuntime.Cache.Insert(entry.Key, expensiveObject, cacheDependency, absoluteExpiration, slidingExpiration, entry.CacheItemUpdateCallback);
                    } 
                    else { 
                        HttpRuntime.Cache.Remove(entry.Key);
                    } 
                }
                catch (Exception e) {
                    HttpRuntime.Cache.Remove(entry.Key);
                    try { 
                        WebBaseEvent.RaiseRuntimeError(e, value);
                    } 
                    catch { 
                    }
                } 
            }
        }

        ///  
        ///    Retrieves an item from the cache.
        ///  
        public object Get(string key) { 
            return _cacheInternal.DoGet(true, key, CacheGetOptions.None);
        } 

        internal object Get(string key, CacheGetOptions getOptions) {
            return _cacheInternal.DoGet(true, key, getOptions);
        } 

 
        ///  
        ///    Inserts an item into the Cache with default values.
        ///  
        public void Insert(string key, object value) {
            _cacheInternal.DoInsert(
                        true,
                        key, 
                        value,
                        null, 
                        NoAbsoluteExpiration, 
                        NoSlidingExpiration,
                        CacheItemPriority.Default, 
                        null,
                        true);
        }
 

        ///  
        /// Inserts an object into the System.Web.Caching.Cache that has file or key 
        ///    dependencies.
        ///  
        public void Insert(string key, object value, CacheDependency dependencies) {
            _cacheInternal.DoInsert(
                        true,
                        key, 
                        value,
                        dependencies, 
                        NoAbsoluteExpiration, 
                        NoSlidingExpiration,
                        CacheItemPriority.Default, 
                        null,
                        true);
        }
 

        ///  
        /// Inserts an object into the System.Web.Caching.Cache that has file or key dependencies and 
        ///    expires at the value set in the  parameter.
        ///  
        public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration) {
            DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
            _cacheInternal.DoInsert(
                        true, 
                        key,
                        value, 
                        dependencies, 
                        utcAbsoluteExpiration,
                        slidingExpiration, 
                        CacheItemPriority.Default,
                        null,
                        true);
        } 

 
        public void Insert( 
                string key,
                object value, 
                CacheDependency dependencies,
                DateTime absoluteExpiration,
                TimeSpan slidingExpiration,
                CacheItemPriority priority, 
                CacheItemRemovedCallback onRemoveCallback) {
 
            DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration); 
            _cacheInternal.DoInsert(
                        true, 
                        key,
                        value,
                        dependencies,
                        utcAbsoluteExpiration, 
                        slidingExpiration,
                        priority, 
                        onRemoveCallback, 
                        true);
        } 

        // DevDiv Bugs 162763:
        // Add a an event that fires *before* an item is evicted from the ASP.NET Cache
        public void Insert( 
                string key,
                object value, 
                CacheDependency dependencies, 
                DateTime absoluteExpiration,
                TimeSpan slidingExpiration, 
                CacheItemUpdateCallback onUpdateCallback) {

            if (dependencies == null && absoluteExpiration == Cache.NoAbsoluteExpiration && slidingExpiration == Cache.NoSlidingExpiration) {
                throw new ArgumentException(SR.GetString(SR.Invalid_Parameters_To_Insert)); 
            }
            if (onUpdateCallback == null) { 
                throw new ArgumentNullException("onUpdateCallback"); 
            }
            DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration); 
            // Insert updatable cache entry
            _cacheInternal.DoInsert (
                        true,
                        key, 
                        value,
                        null, 
                        Cache.NoAbsoluteExpiration, 
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.NotRemovable, 
                        null,
                        true);
            // Ensure the sentinel depends on its updatable entry
            string[] cacheKeys = { key }; 
            CacheDependency expensiveObjectDep = new CacheDependency(null, cacheKeys);
            if (dependencies == null) { 
                dependencies = expensiveObjectDep; 
            }
            else { 
                AggregateCacheDependency deps = new AggregateCacheDependency();
                deps.Add(dependencies, expensiveObjectDep);
                dependencies = deps;
            } 
            // Insert sentinel entry for the updatable cache entry
            _cacheInternal.DoInsert( 
                        false, 
                        CacheInternal.PrefixValidationSentinel + key,
                        new SentinelEntry(key, expensiveObjectDep, onUpdateCallback), 
                        dependencies,
                        utcAbsoluteExpiration,
                        slidingExpiration,
                        CacheItemPriority.NotRemovable, 
                        Cache.s_sentinelRemovedCallback,
                        true); 
        } 

 
        public object Add(
                string key,
                object value,
                CacheDependency dependencies, 
                DateTime absoluteExpiration,
                TimeSpan slidingExpiration, 
                CacheItemPriority priority, 
                CacheItemRemovedCallback onRemoveCallback) {
 
            DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
            return _cacheInternal.DoInsert(
                        true,
                        key, 
                        value,
                        dependencies, 
                        utcAbsoluteExpiration, 
                        slidingExpiration,
                        priority, 
                        onRemoveCallback,
                        false);
        }
 

        ///  
        ///    Removes the specified item from the cache.  
        /// 
        public object Remove(string key) { 
            CacheKey cacheKey = new CacheKey(key, true);
            return _cacheInternal.DoRemove(cacheKey, CacheItemRemovedReason.Removed);
        }
 
        public long EffectivePrivateBytesLimit {
            get { 
                return _cacheInternal.EffectivePrivateBytesLimit; 
            }
        } 

        public long EffectivePercentagePhysicalMemoryLimit {
            get {
                return _cacheInternal.EffectivePercentagePhysicalMemoryLimit; 
            }
        } 
    } 

    class CacheCommon { 
        const int MEMORYSTATUS_INTERVAL_5_SECONDS = 5 * Msec.ONE_SECOND;
        const int MEMORYSTATUS_INTERVAL_30_SECONDS = 30 * Msec.ONE_SECOND;

        internal CacheInternal              _cacheInternal; 
        internal Cache                      _cachePublic;
        internal protected CacheMemoryStats _cacheMemoryStats; 
        private  object                     _timerLock = new object(); 
        private  Timer                      _timer;
        private  int                        _currentPollInterval = MEMORYSTATUS_INTERVAL_30_SECONDS; 
        internal int                        _inCacheManagerThread;
        internal bool                       _enableMemoryCollection;
        internal bool                       _enableExpiration;
        internal bool                       _internalConfigRead; 

        internal CacheCommon() { 
            _cachePublic = new Cache(0); 
            _cacheMemoryStats = new CacheMemoryStats(new SRef(this));
            _enableMemoryCollection = true; 
            _enableExpiration = true;
        }

        internal void Dispose(bool disposing) { 
            if (disposing) {
                EnableCacheMemoryTimer(false); 
                _cacheMemoryStats.Dispose(); 
            }
        } 

        internal void SetCacheInternal(CacheInternal cacheInternal) {
            _cacheInternal = cacheInternal;
            _cachePublic.SetCacheInternal(cacheInternal); 
        }
 
        internal void ReadCacheInternalConfig(CacheSection cacheSection) { 
            if (_internalConfigRead) {
                return; 
            }

            lock (this) {
                if (_internalConfigRead) { 
                    return;
                } 
 
                // Set it to true here so that even if we have to call ReadCacheInternalConfig
                // from the code below, we won't get into an infinite loop. 
                _internalConfigRead = true;

                if (cacheSection != null) {
                    _enableMemoryCollection = (!cacheSection.DisableMemoryCollection); 
                    _enableExpiration = (!cacheSection.DisableExpiration);
                    _cacheMemoryStats.ReadConfig(cacheSection); 
                    _currentPollInterval = CacheMemorySizePressure.PollInterval; 
                    ResetFromConfigSettings();
                } 
            }
        }

        internal void ResetFromConfigSettings() { 
            EnableCacheMemoryTimer(_enableMemoryCollection);
            _cacheInternal.EnableExpirationTimer(_enableExpiration); 
        } 

        internal void EnableCacheMemoryTimer(bool enable) { 
            lock (_timerLock) {
#if DBG
                if (Debug.IsTagPresent("Timer") && !Debug.IsTagEnabled("Timer")) {
                    enable = false; 
                }
 
#endif 

                if (enable) { 

                    if (_timer == null) {
                        //  has not been read yet
                        _timer = new Timer(new TimerCallback(this.CacheManagerTimerCallback), null, _currentPollInterval, _currentPollInterval); 
                        Debug.Trace("Cache", "Started CacheMemoryTimers");
                    } 
                    else { 
                        _timer.Change(_currentPollInterval, _currentPollInterval);
                    } 
                }
                else {
                    Timer timer = _timer;
                    if (timer != null && Interlocked.CompareExchange(ref _timer, null, timer) == timer) { 
                        timer.Dispose();
                        Debug.Trace("Cache", "Stopped CacheMemoryTimers"); 
                    } 
                }
            } 

            if (!enable) {
                // wait for CacheManagerTimerCallback to finish
                while(_inCacheManagerThread != 0) { 
                    Thread.Sleep(100);
                } 
            } 
        }
 
        void AdjustTimer() {
            lock (_timerLock) {

                if (_timer == null) 
                    return;
 
                // the order of these if statements is important 

                // When above the high pressure mark, interval should be 5 seconds or less 
                if (_cacheMemoryStats.IsAboveHighPressure()) {
                    if (_currentPollInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) {
                        _currentPollInterval = MEMORYSTATUS_INTERVAL_5_SECONDS;
                        _timer.Change(_currentPollInterval, _currentPollInterval); 
                    }
                    return; 
                } 

                // When above half the low pressure mark, interval should be 30 seconds or less 
                if ((_cacheMemoryStats.CacheSizePressure.PressureLast > _cacheMemoryStats.CacheSizePressure.PressureLow/2)
                    || (_cacheMemoryStats.TotalMemoryPressure.PressureLast > _cacheMemoryStats.TotalMemoryPressure.PressureLow/2)) {
                    // DevDivBugs 104034: allow interval to fall back down when memory pressure goes away
                    int newPollInterval = Math.Min(CacheMemorySizePressure.PollInterval, MEMORYSTATUS_INTERVAL_30_SECONDS); 
                    if (_currentPollInterval != newPollInterval) {
                        _currentPollInterval = newPollInterval; 
                        _timer.Change(_currentPollInterval, _currentPollInterval); 
                    }
                    return; 
                }

                // there is no pressure, interval should be the value from config
                if (_currentPollInterval != CacheMemorySizePressure.PollInterval) { 
                    _currentPollInterval = CacheMemorySizePressure.PollInterval;
                    _timer.Change(_currentPollInterval, _currentPollInterval); 
                } 
            }
        } 

        void CacheManagerTimerCallback(object state) {
            CacheManagerThread(0);
        } 

        internal long CacheManagerThread(int minPercent) { 
            if (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0) 
                return 0;
#if DBG 
            Debug.Trace("CacheMemory", "**BEG** CacheManagerThread " + HttpRuntime.AppDomainAppIdInternal + ", " + DateTime.Now.ToString("T", CultureInfo.InvariantCulture));
#endif
            try {
                // Dev10 633335: if the timer has been disposed, return without doing anything 
                if (_timer == null)
                    return 0; 
 
                // The timer thread must always call Update so that the CacheManager
                // knows the size of the cache. 
                _cacheMemoryStats.Update();
                AdjustTimer();
                int percent = Math.Max(minPercent, _cacheMemoryStats.GetPercentToTrim());
                long beginTotalCount = _cacheInternal.TotalCount; 
                Stopwatch sw = Stopwatch.StartNew();
                long trimmedOrExpired = _cacheInternal.TrimIfNecessary(percent); 
                sw.Stop(); 
                // 1) don't update stats if the trim happend because MAX_COUNT was exceeded
                // 2) don't update stats unless we removed at least one entry 
                if (percent > 0 && trimmedOrExpired > 0) {
                    _cacheMemoryStats.SetTrimStats(sw.Elapsed.Ticks, beginTotalCount, trimmedOrExpired);
                }
 
#if DBG
                Debug.Trace("CacheMemory", "**END** CacheManagerThread: " + HttpRuntime.AppDomainAppIdInternal 
                            + ", percent=" + percent 
                            + ", beginTotalCount=" + beginTotalCount
                            + ", trimmed=" + trimmedOrExpired 
                            + ", Milliseconds=" + sw.ElapsedMilliseconds);
#endif

#if PERF 
                SafeNativeMethods.OutputDebugString("CacheCommon.CacheManagerThread:"
                                                    + " minPercent= " + minPercent 
                                                    + ", percent= " + percent 
                                                    + ", beginTotalCount=" + beginTotalCount
                                                    + ", trimmed=" + trimmedOrExpired 
                                                    + ", Milliseconds=" + sw.ElapsedMilliseconds + "\n");
#endif
                return trimmedOrExpired;
            } 
            finally {
                Interlocked.Exchange(ref _inCacheManagerThread, 0); 
            } 
        }
    } 

    abstract class CacheInternal : IEnumerable, IDisposable {
        // cache key prefixes - they keep cache keys short and prevent conflicts
 
        // NOTE: Since we already used up all the lowercase letters from 'a' to 'z',
        // we are now using uppercase letters from 'A' to 'Z' 
        internal const string PrefixFIRST                   = "A"; 
        internal const string PrefixResourceProvider        = "A";
        internal const string PrefixMapPathVPPFile          = "Bf"; 
        internal const string PrefixMapPathVPPDir           = "Bd";

        // Next prefix goes here, until we get to 'Z'
 
        internal const string PrefixOutputCache             = "a";
        internal const string PrefixSqlCacheDependency      = "b"; 
        internal const string PrefixMemoryBuildResult       = "c"; 
        internal const string PrefixPathData                = "d";
        internal const string PrefixHttpCapabilities        = "e"; 
        internal const string PrefixMapPath                 = "f";
        internal const string PrefixHttpSys                 = "g";
        internal const string PrefixFileSecurity            = "h";
        internal const string PrefixInProcSessionState      = "j"; 
        internal const string PrefixStateApplication        = "k";
        internal const string PrefixPartialCachingControl   = "l"; 
        internal const string UNUSED                        = "m"; 
        internal const string PrefixAdRotator               = "n";
        internal const string PrefixWebServiceDataSource    = "o"; 
        internal const string PrefixLoadXPath               = "p";
        internal const string PrefixLoadXml                 = "q";
        internal const string PrefixLoadTransform           = "r";
        internal const string PrefixAspCompatThreading      = "s"; 
        internal const string PrefixDataSourceControl       = "u";
        internal const string PrefixValidationSentinel      = "w"; 
        internal const string PrefixWebEventResource        = "x"; 
        internal const string PrefixAssemblyPath            = "y";
        internal const string PrefixBrowserCapsHash         = "z"; 
        internal const string PrefixLAST                    = "z";

        protected CacheCommon _cacheCommon;
        private int _disposed; 

        // virtual methods requiring implementation 
        internal abstract int PublicCount   {get;} 

        internal abstract long TotalCount   {get;} 

        internal abstract IDictionaryEnumerator CreateEnumerator();

        internal abstract CacheEntry UpdateCache( 
                CacheKey                cacheKey,
                CacheEntry              newEntry, 
                bool                    replace, 
                CacheItemRemovedReason  removedReason,
                out object              valueOld); 

        internal abstract long TrimIfNecessary(int percent);

        internal abstract void EnableExpirationTimer(bool enable); 

        // If UseMemoryCache is true, we will direct all ASP.NET 
        // cache usage into System.Runtime.Caching.dll.  This allows 
        // us to test System.Runtime.Caching.dll with all existing
        // ASP.NET test cases (functional, perf, and stress). 
#if USE_MEMORY_CACHE
        private static bool _useMemoryCache;
        private static volatile bool _useMemoryCacheInited;
        internal static bool UseMemoryCache { 
            get {
                if (!_useMemoryCacheInited) { 
                    RegistryKey regKey = null; 
                    try {
                        regKey = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\ASP.NET"); 
                        if (regKey != null) {
                            if ((int)regKey.GetValue("UseMemoryCache", 0)== 1) {
                                _useMemoryCache = true;
                            } 
                        }
                    } 
                    finally { 
                        if (regKey != null) {
                            regKey.Close(); 
                        }
                    }
                    _useMemoryCacheInited = true;
                } 
                return _useMemoryCache;
            } 
        } 
#endif
 
        // common implementation
        static internal CacheInternal Create() {
            CacheCommon cacheCommon = new CacheCommon();
            CacheInternal cacheInternal; 
#if USE_MEMORY_CACHE
            if (UseMemoryCache) { 
                cacheInternal = new MemCache(cacheCommon); 
            }
            else { 
#endif
                int numSubCaches = 0;
                uint numCPUs = (uint) SystemInfo.GetNumProcessCPUs();
                // the number of subcaches is the minimal power of 2 greater 
                // than or equal to the number of cpus
                numSubCaches = 1; 
                numCPUs -= 1; 
                while (numCPUs > 0) {
                    numSubCaches <<= 1; 
                    numCPUs >>= 1;
                }
                if (numSubCaches == 1) {
                    cacheInternal = new CacheSingle(cacheCommon, null, 0); 
                }
                else { 
                    cacheInternal = new CacheMultiple(cacheCommon, numSubCaches); 
                }
#if USE_MEMORY_CACHE 
            }
#endif
            cacheCommon.SetCacheInternal(cacheInternal);
            cacheCommon.ResetFromConfigSettings(); 

            return cacheInternal; 
        } 

        protected CacheInternal(CacheCommon cacheCommon) { 
            _cacheCommon = cacheCommon;
        }

        protected virtual void Dispose(bool disposing) { 
            _cacheCommon.Dispose(disposing);
        } 
 
        public void Dispose() {
            _disposed = 1; 
            Dispose(true);
            // no destructor, don't need it.
            // System.GC.SuppressFinalize(this);
        } 

        internal bool IsDisposed { get { return _disposed == 1; } } 
 
        internal void ReadCacheInternalConfig(CacheSection cacheSection) {
            _cacheCommon.ReadCacheInternalConfig(cacheSection); 
        }

        internal long TrimCache(int percent) {
            return _cacheCommon.CacheManagerThread(percent); 
        }
 
        internal Cache CachePublic { 
            get {return _cacheCommon._cachePublic;}
        } 

        internal long EffectivePrivateBytesLimit {
            get { return _cacheCommon._cacheMemoryStats.CacheSizePressure.MemoryLimit; }
        } 

        internal long EffectivePercentagePhysicalMemoryLimit { 
            get { return _cacheCommon._cacheMemoryStats.TotalMemoryPressure.MemoryLimit; } 
        }
 
        IEnumerator IEnumerable.GetEnumerator() {
            return CreateEnumerator();
        }
 
        public IDictionaryEnumerator GetEnumerator() {
            return CreateEnumerator(); 
        } 

        internal object this[string key] { 
            get {
                return Get(key);
            }
        } 

        internal object Get(string key) { 
            return DoGet(false, key, CacheGetOptions.None); 
        }
 
        internal object Get(string key, CacheGetOptions getOptions) {
            return DoGet(false, key, getOptions);
        }
 
        internal object DoGet(bool isPublic, string key, CacheGetOptions getOptions) {
            CacheEntry  entry; 
            CacheKey    cacheKey; 
            object      dummy;
 
            cacheKey = new CacheKey(key, isPublic);
            entry = UpdateCache(cacheKey, null, false, CacheItemRemovedReason.Removed, out dummy);
            if (entry != null) {
                if ((getOptions & CacheGetOptions.ReturnCacheEntry) != 0) { 
                    return entry;
                } 
                else { 
                    return entry.Value;
                } 
            }
            else {
                return null;
            } 
        }
 
        internal void UtcInsert(string key, object value) { 
            DoInsert(false,
                     key, 
                     value,
                     null,
                     Cache.NoAbsoluteExpiration,
                     Cache.NoSlidingExpiration, 
                     CacheItemPriority.Default,
                     null, 
                     true); 

        } 

        internal void UtcInsert(string key, object value, CacheDependency dependencies) {
            DoInsert(false,
                     key, 
                     value,
                     dependencies, 
                     Cache.NoAbsoluteExpiration, 
                     Cache.NoSlidingExpiration,
                     CacheItemPriority.Default, 
                     null,
                     true);
        }
 
        internal void UtcInsert(
                string key, 
                object value, 
                CacheDependency dependencies,
                DateTime utcAbsoluteExpiration, 
                TimeSpan slidingExpiration) {

            DoInsert(false,
                     key, 
                     value,
                     dependencies, 
                     utcAbsoluteExpiration, 
                     slidingExpiration,
                     CacheItemPriority.Default, 
                     null,
                     true);
        }
 
        internal void UtcInsert(
                string key, 
                object value, 
                CacheDependency dependencies,
                DateTime utcAbsoluteExpiration, 
                TimeSpan slidingExpiration,
                CacheItemPriority priority,
                CacheItemRemovedCallback onRemoveCallback) {
 
            DoInsert(false,
                     key, 
                     value, 
                     dependencies,
                     utcAbsoluteExpiration, 
                     slidingExpiration,
                     priority,
                     onRemoveCallback,
                     true); 
        }
 
        internal object UtcAdd( 
                string key,
                object value, 
                CacheDependency dependencies,
                DateTime utcAbsoluteExpiration,
                TimeSpan slidingExpiration,
                CacheItemPriority priority, 
                CacheItemRemovedCallback onRemoveCallback) {
 
            return DoInsert( 
                        false,
                        key, 
                        value,
                        dependencies,
                        utcAbsoluteExpiration,
                        slidingExpiration, 
                        priority,
                        onRemoveCallback, 
                        false); 

        } 

        internal object DoInsert(
                bool isPublic,
                string key, 
                object value,
                CacheDependency dependencies, 
                DateTime utcAbsoluteExpiration, 
                TimeSpan slidingExpiration,
                CacheItemPriority priority, 
                CacheItemRemovedCallback onRemoveCallback,
                bool replace) {

 
            /*
             * If we throw an exception, prevent a leak by a user who 
             * writes the following: 
             *
             *     Cache.Insert(key, value, new CacheDependency(file)); 
             */
            using (dependencies) {
                CacheEntry      entry;
                object          dummy; 

                entry = new CacheEntry( 
                        key, 
                        value,
                        dependencies, 
                        onRemoveCallback,
                        utcAbsoluteExpiration,
                        slidingExpiration,
                        priority, 
                        isPublic);
 
                entry = UpdateCache(entry, entry, replace, CacheItemRemovedReason.Removed, out dummy); 

                /* 
                 * N.B. A set can fail if two or more threads set the same key
                 * at the same time.
                 */
#if DBG 
                if (replace) {
                    string yesno = (entry != null) ? "succeeded" : "failed"; 
                    Debug.Trace("CacheAPIInsert", "Cache.Insert " + yesno + ": " + key); 
                }
                else { 
                    if (entry == null) {
                        Debug.Trace("CacheAPIAdd", "Cache.Add added new item: " + key);
                    }
                    else { 
                        Debug.Trace("CacheAPIAdd", "Cache.Add returned existing item: " + key);
                    } 
                } 
#endif
 
                if (entry != null) {
                    return entry.Value;
                }
                else { 
                    return null;
                } 
            } 
        }
 
        internal object Remove(string key) {
            CacheKey cacheKey = new CacheKey(key, false);
            return DoRemove(cacheKey, CacheItemRemovedReason.Removed);
        } 

        internal object Remove(CacheKey cacheKey, CacheItemRemovedReason reason)  { 
            return DoRemove(cacheKey, reason); 
        }
 
        /*
         * Remove an item from the cache, with a specific reason.
         * This is package access so only the cache can specify
         * a reason other than REMOVED. 
         *
         * @param key The key for the item. 
         * @exception ArgumentException 
         */
        internal object DoRemove(CacheKey cacheKey, CacheItemRemovedReason reason)  { 
            object      valueOld;

            UpdateCache(cacheKey, null, true, reason, out valueOld);
 
#if DBG
            if (valueOld != null) { 
                Debug.Trace("CacheAPIRemove", "Cache.Remove succeeded, reason=" + reason + ": " + cacheKey); 
            }
            else { 
                Debug.Trace("CacheAPIRemove", "Cache.Remove failed, reason=" + reason + ": " + cacheKey);
            }
#endif
 
            return valueOld;
        } 
    } 

    sealed class CacheKeyComparer : IEqualityComparer  { 
        static CacheKeyComparer    s_comparerInstance;

        static internal CacheKeyComparer GetInstance() {
            if (s_comparerInstance == null) { 
                s_comparerInstance = new CacheKeyComparer();
            } 
 
            return s_comparerInstance;
        } 

        private CacheKeyComparer()
        {
        } 

        bool IEqualityComparer.Equals(Object x, Object y) 
        { 
            return Compare(x, y) == 0;
        } 

        // Compares two objects. An implementation of this method must return a
        // value less than zero if x is less than y, zero if x is equal to y, or a
        // value greater than zero if x is greater than y. 
        private int Compare(Object x, Object y) {
            CacheKey  a, b; 
 
            Debug.Assert(x != null && x is CacheKey);
            Debug.Assert(y != null && y is CacheKey); 

            a = (CacheKey) x;
            b = (CacheKey) y;
 
            if (a.IsPublic) {
                if (b.IsPublic) { 
                    return String.Compare(a.Key, b.Key, StringComparison.Ordinal); 
                }
                else { 
                    return 1;
                }
            }
            else { 
                if (!b.IsPublic) {
                    return String.Compare(a.Key, b.Key, StringComparison.Ordinal); 
                } 
                else {
                    return -1; 
                }
            }
        }
        // Returns a hash code for the given object. 
        //
        int IEqualityComparer.GetHashCode(Object obj) { 
            Debug.Assert(obj != null && obj is CacheKey); 

            CacheKey cacheKey = (CacheKey) obj; 

            return cacheKey.GetHashCode();
        }
    } 

    /* 
     * The cache. 
     */
    sealed class CacheSingle : CacheInternal { 
        // cache stats
        static readonly TimeSpan    INSERT_BLOCK_WAIT = new TimeSpan(0, 0, 10);
        const int                   MAX_COUNT = Int32.MaxValue / 2;
        const int                   MIN_COUNT = 10; 

 
        Hashtable           _entries;           /* lookup table of entries */ 
        CacheExpires        _expires;           /* expires tables */
        CacheUsage          _usage;             /* usage tables */ 
        object              _lock;              /* read/write synchronization for _entries */
        int                 _disposed;          /* disposed */
        int                 _totalCount;        /* count of total entries */
        int                 _publicCount;       /* count of public entries */ 
        ManualResetEvent    _insertBlock;       /* event to block inserts during high mem usage */
        bool                _useInsertBlock;    /* use insert block? */ 
        int                 _insertBlockCalls;  /* number of callers using insert block */ 
        int                 _iSubCache;         /* index of this cache */
        CacheMultiple       _cacheMultiple;     /* the CacheMultiple containing this cache */ 

        /*
         * Constructs a new Cache.
         */ 
        internal CacheSingle(CacheCommon cacheCommon, CacheMultiple cacheMultiple, int iSubCache) : base(cacheCommon) {
            _cacheMultiple = cacheMultiple; 
            _iSubCache = iSubCache; 
            _entries = new Hashtable(CacheKeyComparer.GetInstance());
            _expires = new CacheExpires(this); 
            _usage = new CacheUsage(this);
            _lock = new object();
            _insertBlock = new ManualResetEvent(true);
        } 

        /* 
         * 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);
        } 

        // Get the insert block manual reset event if it has not been disposed.
        ManualResetEvent UseInsertBlock() {
            for (;;) { 
                if (_disposed == 1)
                    return null; 
 
                int n = _insertBlockCalls;
                if (n < 0) { 
                    return null;
                }

                if (Interlocked.CompareExchange(ref _insertBlockCalls, n + 1, n) == n) { 
                    return _insertBlock;
                } 
            } 
        }
 
        // Release the insert block event, and dispose it if it has been released
        // more times than it has been used
        void ReleaseInsertBlock() {
            if (Interlocked.Decrement(ref _insertBlockCalls) < 0) { 
                ManualResetEvent e = _insertBlock;
                _insertBlock = null; 
 
                // now close
                e.Close(); 
            }
        }

        // Set the insert block event. 
        void SetInsertBlock() {
            ManualResetEvent e = null; 
            try { 
                e = UseInsertBlock();
                if (e != null) { 
                    e.Set();
                }
            }
            finally { 
                if (e != null) {
                    ReleaseInsertBlock(); 
                } 
            }
        } 

        // Reset the insert block event.
        void ResetInsertBlock() {
            ManualResetEvent e = null; 
            try {
                e = UseInsertBlock(); 
                if (e != null) { 
                    e.Reset();
                } 
            }
            finally {
                if (e != null) {
                    ReleaseInsertBlock(); 
                }
            } 
        } 

        // Wait on the insert block event. 
        bool WaitInsertBlock() {
            bool signaled = false;
            ManualResetEvent e = null;
            try { 
                e = UseInsertBlock();
                if (e != null) { 
                    Debug.Trace("CacheMemoryTrimInsertBlock", "WaitInsertBlock: Cache " + _iSubCache + ": _useInsertBlock=true"); 
                    signaled = e.WaitOne(INSERT_BLOCK_WAIT, false);
                    Debug.Trace("CacheMemoryTrimInsertBlock", "Done waiting"); 
                }
            }
            finally {
                if (e != null) { 
                    ReleaseInsertBlock();
                } 
            } 

            return signaled; 
        }

        internal void BlockInsertIfNeeded() {
            if (_cacheCommon._cacheMemoryStats.IsAboveHighPressure()) { 
                Debug.Trace("CacheMemoryTrimInsertBlock", "BlockInsertIfNeeded: Cache " + _iSubCache + ": _useInsertBlock=true");
                _useInsertBlock = true; 
                ResetInsertBlock(); 
            }
        } 

        internal void UnblockInsert() {
            if (_useInsertBlock) {
                _useInsertBlock = false; 
                SetInsertBlock();
                Debug.Trace("CacheMemoryTrimInsertBlock", "UnblockInsert: Cache " + _iSubCache + ": _useInsertBlock=false"); 
            } 
        }
 

        internal override int PublicCount {
            get {return _publicCount;}
        } 

        internal override long TotalCount { 
            get {return _totalCount;} 
        }
 
        internal override IDictionaryEnumerator CreateEnumerator() {
            Hashtable h = new Hashtable(_publicCount);
            DateTime utcNow = DateTime.UtcNow;
 
            lock (_lock) {
                foreach (DictionaryEntry d in _entries) { 
                    CacheEntry entry = (CacheEntry) d.Value; 

                    // note that ASP.NET does not use this enumerator internally, 
                    // so we just choose public items.
                    if (entry.IsPublic &&
                        entry.State == CacheEntry.EntryState.AddedToCache &&
                        ((!_cacheCommon._enableExpiration) || (utcNow <= entry.UtcExpires))) { 
                        h[entry.Key] = entry.Value;
                    } 
                } 
            }
 
            return h.GetEnumerator();
        }

        /* 
         * 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; 
        }
 
        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); 
                        }
                    }
                }
            } 
        }
 
        internal override long TrimIfNecessary(int percent) { 
            Debug.Assert(_cacheCommon._inCacheManagerThread == 1, "Trim should only occur when we're updating memory statistics.");
            if (!_cacheCommon._enableMemoryCollection) 
                return 0;

            int toTrim = 0;
            // do we need to drop a percentage of entries? 
            if (percent > 0) {
                toTrim = (int)(((long)_totalCount * (long)percent) / 100L); 
            } 
            // would this leave us above MAX_COUNT?
            int minTrim = _totalCount - MAX_COUNT; 
            if (toTrim < minTrim) {
                toTrim = minTrim;
            }
            // would this put us below MIN_COUNT? 
            int maxTrim = _totalCount - MIN_COUNT;
            if (toTrim > maxTrim) { 
                toTrim = maxTrim; 
            }
            // do we need to trim? 
            if (toTrim <= 0 || HostingEnvironment.ShutdownInitiated) {
                return 0;
            }
 
            int ocEntriesTrimmed = 0; // number of output cache entries trimmed
            int publicEntriesTrimmed = 0; // number of public entries trimmed 
            int totalTrimmed = 0; // total number of entries trimmed 
            int trimmedOrExpired = 0;
            int beginTotalCount = _totalCount; 

            try {
                trimmedOrExpired = _expires.FlushExpiredItems(true);
                if (trimmedOrExpired < toTrim) { 
                    totalTrimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired, ref publicEntriesTrimmed, ref ocEntriesTrimmed);
                    trimmedOrExpired += totalTrimmed; 
                } 

                if (totalTrimmed > 0) { 
                    // Update values for perfcounters
                    PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_TOTAL_TRIMS, totalTrimmed);
                    PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_API_TRIMS, publicEntriesTrimmed);
                    PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_OUTPUT_TRIMS, ocEntriesTrimmed); 
                }
            } 
            catch { 
            }
 
#if DBG
            Debug.Trace("CacheMemory", "TrimIfNecessary: _iSubCache= " + _iSubCache
                        + ", beginTotalCount=" + beginTotalCount
                        + ", endTotalCount=" + _totalCount 
                        + ", percent=" + percent
                        + ", trimmed=" + totalTrimmed); 
#endif 
            return trimmedOrExpired;
        } 

        internal override void EnableExpirationTimer(bool enable) {
            if (_expires != null) {
                _expires.EnableExpirationTimer(enable); 
            }
        } 
    } 

    class CacheMultiple : CacheInternal { 
        int             _disposed;
        CacheSingle[]   _caches;
        int             _cacheIndexMask;
 
        internal CacheMultiple(CacheCommon cacheCommon, int numSingleCaches) : base(cacheCommon) {
            Debug.Assert(numSingleCaches > 1, "numSingleCaches is not greater than 1"); 
            Debug.Assert((numSingleCaches & (numSingleCaches - 1)) == 0, "numSingleCaches is not a power of 2"); 
            _cacheIndexMask = numSingleCaches - 1;
            _caches = new CacheSingle[numSingleCaches]; 
            for (int i = 0; i < numSingleCaches; i++) {
                _caches[i] = new CacheSingle(cacheCommon, this, i);
            }
        } 

        protected override void Dispose(bool disposing) { 
            if (disposing) { 
                if (Interlocked.Exchange(ref _disposed, 1) == 0) {
                    foreach (CacheSingle cacheSingle in _caches) { 
                        cacheSingle.Dispose();
                    }
                }
            } 

            base.Dispose(disposing); 
        } 

        internal override int PublicCount { 
            get {
                int count = 0;
                foreach (CacheSingle cacheSingle in _caches) {
                    count += cacheSingle.PublicCount; 
                }
 
                return count; 
            }
        } 

        internal override long TotalCount {
            get {
                long count = 0; 
                foreach (CacheSingle cacheSingle in _caches) {
                    count += cacheSingle.TotalCount; 
                } 

                return count; 
            }
        }

        internal override IDictionaryEnumerator CreateEnumerator() { 
            IDictionaryEnumerator[] enumerators = new IDictionaryEnumerator[_caches.Length];
            for (int i = 0, c = _caches.Length; i < c; i++) { 
                enumerators[i] = _caches[i].CreateEnumerator(); 
            }
 
            return new AggregateEnumerator(enumerators);
        }

        internal CacheSingle GetCacheSingle(int hashCode) { 
            Debug.Assert(_caches != null && _caches.Length != 0);
 
            hashCode = Math.Abs(hashCode); 

            int index = (hashCode & _cacheIndexMask); 
            return _caches[index];
        }

        internal override CacheEntry UpdateCache( 
                CacheKey cacheKey,
                CacheEntry newEntry, 
                bool replace, 
                CacheItemRemovedReason removedReason,
                out object valueOld) { 

            int hashCode = cacheKey.Key.GetHashCode();
            CacheSingle cacheSingle = GetCacheSingle(hashCode);
            return cacheSingle.UpdateCache(cacheKey, newEntry, replace, removedReason, out valueOld); 
        }
 
        internal override long TrimIfNecessary(int percent) { 
            long count = 0;
            foreach (CacheSingle cacheSingle in _caches) { 
                count += cacheSingle.TrimIfNecessary(percent);
            }
            return count;
        } 

        internal override void EnableExpirationTimer(bool enable) { 
            foreach (CacheSingle cacheSingle in _caches) { 
                cacheSingle.EnableExpirationTimer(enable);
            } 
        }
    }

    class AggregateEnumerator : IDictionaryEnumerator { 
        IDictionaryEnumerator []    _enumerators;
        int                         _iCurrent; 
 
        internal AggregateEnumerator(IDictionaryEnumerator [] enumerators) {
            _enumerators = enumerators; 
        }

        public bool MoveNext() {
            bool more; 

            for (;;) { 
                more = _enumerators[_iCurrent].MoveNext(); 
                if (more)
                    break; 

                if (_iCurrent == _enumerators.Length - 1)
                    break;
 
                _iCurrent++;
            } 
 
            return more;
        } 

        public void Reset() {
            for (int i = 0; i <= _iCurrent; i++) {
                _enumerators[i].Reset(); 
            }
 
            _iCurrent = 0; 
        }
 
        public Object Current {
            get {
                return _enumerators[_iCurrent].Current;
            } 
        }
 
        public Object Key { 
            get {
                return _enumerators[_iCurrent].Key; 
            }
        }

        public Object Value { 
            get {
                return _enumerators[_iCurrent].Value; 
            } 
        }
 
    	public DictionaryEntry Entry {
            get {
                return _enumerators[_iCurrent].Entry;
            } 
        }
    } 
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK