QueryCacheManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Common / QueryCache / QueryCacheManager.cs / 1 / QueryCacheManager.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....], [....]
//----------------------------------------------------------------------------- 
 
namespace System.Data.Common.QueryCache
{ 
    using System;
    using System.Collections.Generic;
    using System.Data.EntityClient;
    using System.Data.Metadata.Edm; 
    using System.Data.Objects.Internal;
    using System.Diagnostics; 
    using System.Threading; 
    using System.Data.Common.Internal.Materialization;
 
    /// 
    /// Provides Query Execution Plan Caching Service
    /// 
    ///  
    /// Thread safe.
    /// Dispose must be called as there is no finalizer for this class 
    ///  
    internal class QueryCacheManager : IDisposable
    { 
        #region Constants/Default values for configuration parameters

        /// 
        /// Default Soft maximum number of entries in the cache 
        /// Default value: 1000
        ///  
        const int DefaultMaxNumberOfEntries = 1000; 

        ///  
        /// Default high mark for starting sweeping process
        /// default value: 80% of MaxNumberOfEntries
        /// 
        const float DefaultHighMarkPercentageFactor = 0.8f; // 80% of MaxLimit 

        ///  
        /// Recycler timer period 
        /// 
        const int DefaultRecyclerPeriodInMilliseconds = 60 * 1000; 

        #endregion

        #region Fields 

        ///  
        /// cache lock object 
        /// 
        private readonly object _cacheDataLock = new object(); 

        /// 
        /// cache data
        ///  
        private readonly Dictionary _cacheData = new Dictionary(32);
 
        ///  
        /// soft maximum number of entries in the cache
        ///  
        private readonly int _maxNumberOfEntries;

        /// 
        /// high mark of the number of entries to trigger the sweeping process 
        /// 
        private readonly int _sweepingTriggerHighMark; 
 
        /// 
        /// Eviction timer 
        /// 
        private readonly EvictionTimer _evictionTimer;

        #endregion 

        #region Construction and Initialization 
 
        /// 
        /// Constructs a new Query Cache Manager instance, with default values for all 'configurable' parameters. 
        /// 
        /// A new instance of  configured with default entry count, load factor and recycle period
        internal static QueryCacheManager Create()
        { 
            return new QueryCacheManager(DefaultMaxNumberOfEntries, DefaultHighMarkPercentageFactor, DefaultRecyclerPeriodInMilliseconds);
        } 
 
        /// 
        /// Cache Constructor 
        /// 
        /// 
        ///   Maximum number of entries that the cache should contain.
        ///  
        /// 
        ///   The number of entries that must be present, as a percentage, before entries should be removed 
        ///   according to the eviction policy. 
        ///   Must be greater than 0 and less than or equal to 1.0
        ///  
        /// 
        ///   The interval, in milliseconds, at which the number of entries will be compared to the load factor
        ///   and eviction carried out if necessary.
        ///  
        private QueryCacheManager(int maximumSize, float loadFactor, int recycleMillis)
        { 
            Debug.Assert(maximumSize > 0, "Maximum size must be greater than zero"); 
            Debug.Assert(loadFactor > 0 && loadFactor <= 1, "Load factor must be greater than 0.0 and less than or equal to 1.0");
            Debug.Assert(recycleMillis >= 0, "Recycle period in milliseconds must not be negative"); 

            //
            // Load hardcoded defaults
            // 
            this._maxNumberOfEntries = maximumSize;
 
            // 
            // set sweeping high mark trigger value
            // 
            this._sweepingTriggerHighMark = (int)(_maxNumberOfEntries * loadFactor);

            //
            // Initialize Recycler 
            //
            this._evictionTimer = new EvictionTimer(this, recycleMillis); 
        } 

        #endregion 

        #region 'External' interface
        /// 
        /// Adds new entry to the cache using "abstract" cache context and 
        /// value; returns an existing entry if the key is already in the
        /// dictionary. 
        ///  
        /// 
        ///  
        /// The existing entry in the dicitionary if already there;
        /// inQueryCacheEntry if none was found and inQueryCacheEntry
        /// was added instead.
        ///  
        /// true if the output entry was already found; false if it had to be added.
        internal bool TryLookupAndAdd(QueryCacheEntry inQueryCacheEntry, out QueryCacheEntry outQueryCacheEntry) 
        { 
            Debug.Assert(null != inQueryCacheEntry, "qEntry must not be null");
 
            outQueryCacheEntry = null;

            lock (_cacheDataLock)
            { 
                if (!_cacheData.TryGetValue(inQueryCacheEntry.QueryCacheKey, out outQueryCacheEntry))
                { 
                    // 
                    // add entry to cache data
                    // 
                    _cacheData.Add(inQueryCacheEntry.QueryCacheKey, inQueryCacheEntry);

                    return false;
                } 
                else
                { 
                    outQueryCacheEntry.QueryCacheKey.UpdateHit(); 

                    return true; 
                }
            }
        }
 
        /// 
        /// Lookup service for EntityClient cache key 
        ///  
        /// 
        ///  
        /// 
        internal bool TryCacheLookup(EntityClientCacheKey entityClientCacheKey, out EntityCommandDefinition entityCommandDefinition)
        {
            Debug.Assert(null != entityClientCacheKey, "entityClientCacheKey must not be null"); 

            entityCommandDefinition = null; 
 
            QueryCacheEntry qEntry = null;
 
            //
            // invoke internal lookup
            //
            bool bHit = TryInternalCacheLookup(entityClientCacheKey, out qEntry); 

            // 
            // if it is a hit, 'extract' the entry strong type cache value 
            //
            if (bHit) 
            {
                entityCommandDefinition = ((EntityClientCacheEntry)qEntry).GetTarget();
            }
 
            return bHit;
        } 
 
        /// 
        /// Lookup service for ShaperFactoryQueryCacheKey 
        /// 
        /// 
        /// 
        ///  
        /// 
        internal bool TryCacheLookup(ShaperFactoryQueryCacheKey key, out ShaperFactory factory) 
        { 
            Debug.Assert(null != key, "key null");
            QueryCacheEntry cacheEntry; 
            if (TryInternalCacheLookup(key, out cacheEntry))
            {
                factory = (ShaperFactory)cacheEntry.GetTarget();
                return true; 
            }
            factory = null; 
            return false; 
        }
 
        /// 
        /// Lookup service for Entity-SQL ObjectQuery cache key
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        internal bool TryCacheLookup(EntitySqlQueryCacheKey objectQueryCacheKey, out ObjectQueryExecutionPlan execPlan) 
        {
            Debug.Assert(null != objectQueryCacheKey, "objectQueryCacheKey must not be null");

            execPlan = null; 
            QueryCacheEntry qEntry = null;
 
            // 
            // invoke internal lookup
            // 
            bool bHit = TryInternalCacheLookup(objectQueryCacheKey, out qEntry);

            //
            // if it is a hit, 'extract' the entry strong type cache value 
            //
            if (bHit) 
            { 
                execPlan = ((EntitySqlQueryCacheEntry)qEntry).ExecutionPlan;
            } 

            return bHit;
        }
 
        /// 
        /// Lookup service for CompiledQuery cache key 
        ///  
        /// 
        ///  
        /// 
        /// 
        /// 
        internal bool TryCacheLookup(CompiledQueryCacheKey objectQueryCacheKey, out CompiledQueryCacheEntry cacheEntry) 
        {
            Debug.Assert(null != objectQueryCacheKey, "objectQueryCacheKey must not be null"); 
 
            cacheEntry = null;
            QueryCacheEntry qEntry = null; 

            //
            // invoke internal lookup
            // 
            bool bHit = TryInternalCacheLookup(objectQueryCacheKey, out qEntry);
 
            // 
            // if it is a hit, 'extract' the entry strong type cache value
            // 
            if (bHit)
            {
                cacheEntry = (CompiledQueryCacheEntry)qEntry;
            } 

            return bHit; 
        } 

        ///  
        /// Clears the Cache
        /// 
        internal void Clear()
        { 
            lock (_cacheDataLock)
            { 
                _cacheData.Clear(); 
            }
        } 
        #endregion

        #region Private Members
 
        /// 
        /// lookup service 
        ///  
        /// 
        ///  
        /// true if cache hit, false if cache miss
        private bool TryInternalCacheLookup( QueryCacheKey queryCacheKey, out QueryCacheEntry queryCacheEntry )
        {
            Debug.Assert(null != queryCacheKey, "queryCacheKey must not be null"); 

            queryCacheEntry = null; 
 
            bool bHit = false;
 
            //
            // lock the cache for the minimal possible period
            //
            lock (_cacheDataLock) 
            {
                bHit = _cacheData.TryGetValue(queryCacheKey, out queryCacheEntry); 
            } 

            // 
            // if cache hit
            //
            if (bHit)
            { 
                //
                // update hit mark in cache key 
                // 
                queryCacheEntry.QueryCacheKey.UpdateHit();
            } 

            return bHit;
        }
 

        ///  
        /// Recycler handler. This method is called directly by the eviction timer. 
        /// It should take no action beyond invoking the  method on the
        /// cache manager instance passed as . 
        /// 
        /// The cache manager instance on which the 'recycle' handler should be invoked
        private static void CacheRecyclerHandler(object state)
        { 
            ((QueryCacheManager)state).SweepCache();
        } 
 
        /// 
        /// Aging factor 
        /// 
        private static readonly int[] _agingFactor = {1,1,2,4,8,16};
        private static readonly int AgingMaxIndex = _agingFactor.Length - 1;
 
        /// 
        /// Sweeps the cache removing old unused entries. 
        /// This method implements the query cache eviction policy. 
        /// 
        private void SweepCache() 
        {
            //
            // Disable eviction timer
            // 
            if (!this._evictionTimer.Suspend())
            { 
                // Return value of false indicates the manager and timer have been disposed. 
                return;
            } 

            lock (_cacheDataLock)
            {
                // 
                // recycle only if entries exceeds the high mark factor
                // 
                if (_cacheData.Count > _sweepingTriggerHighMark) 
                {
                    // 
                    // sweep the cache
                    //
                    uint evictedEntriesCount = 0;
                    List cacheKeys = new List(_cacheData.Count); 
                    Debug.Assert(GC.GetGeneration(cacheKeys) < 2, "we should not allocate temp object in the LOH");
 
                    cacheKeys.AddRange(_cacheData.Keys); 
                    for (int i = 0; i < cacheKeys.Count; i++)
                    { 
                        //
                        // if entry was not used in the last time window, then evict the entry
                        //
                        if (0 == cacheKeys[i].HitCount) 
                        {
                            _cacheData.Remove(cacheKeys[i]); 
                            evictedEntriesCount++; 
                        }
                        // 
                        // otherwise, age the entry in a progressive scheme
                        //
                        else
                        { 
                            int agingIndex = unchecked(cacheKeys[i].AgingIndex + 1);
                            if (agingIndex > AgingMaxIndex) 
                            { 
                                agingIndex = AgingMaxIndex;
                            } 
                            cacheKeys[i].AgingIndex = agingIndex;
                            cacheKeys[i].HitCount = cacheKeys[i].HitCount >> _agingFactor[agingIndex];
                        }
                    } 
                }
            } 
 
            //
            // Enable eviction timer 
            //
            this._evictionTimer.Resume();
        }
 
        #endregion
 
        #region IDisposable Members 

        ///  
        /// Dispose instance
        /// 
        /// Dispose must be called as there are no finalizers for this class
        public void Dispose() 
        {
            if (this._evictionTimer.Stop()) 
            { 
                this.Clear();
            } 
        }

        #endregion
 
        /// 
        /// Periodically invokes cache cleanup logic on a specified  instance, 
        /// and allows this periodic callback to be suspended, resumed or stopped in a thread-safe way. 
        /// 
        private sealed class EvictionTimer 
        {
            /// Used to control multi-threaded accesses to this instance
            private readonly object _[....] = new object();
 
            /// The required interval between invocations of the cache cleanup logic
            private readonly int _period; 
 
            /// The underlying  that implements the periodic callback
            private Timer _timer; 

            internal EvictionTimer(QueryCacheManager cacheManager, int recyclePeriod)
            {
                this._period = recyclePeriod; 
                this._timer = new Timer(QueryCacheManager.CacheRecyclerHandler, cacheManager, recyclePeriod, recyclePeriod);
            } 
 
            /// 
            /// Permanently stops the eviction timer. 
            /// It will no longer generate periodic callbacks and further calls to , , or ,
            /// though thread-safe, will have no effect.
            /// 
            ///  
            ///   If this eviction timer has already been stopped (using the  method), returns false;
            ///   otherwise, returns true to indicate that the call successfully stopped and cleaned up the underlying timer instance. 
            ///  
            /// 
            ///   Thread safe. May be called regardless of the current state of the eviction timer. 
            ///   Once stopped, an eviction timer cannot be restarted with the  method.
            /// 
            internal bool Stop()
            { 
                lock (_[....])
                { 
                    if (this._timer != null) 
                    {
                        this._timer.Dispose(); 
                        this._timer = null;
                        return true;
                    }
                    else 
                    {
                        return false; 
                    } 
                }
            } 

            /// 
            /// Pauses the operation of the eviction timer.
            ///  
            /// 
            ///   If this eviction timer has already been stopped (using the  method), returns false; 
            ///   otherwise, returns true to indicate that the call successfully suspended the inderlying  
            ///   and no further periodic callbacks will be generated until the  method is called.
            ///  
            /// 
            ///   Thread-safe. May be called regardless of the current state of the eviction timer.
            ///   Once suspended, an eviction timer may be resumed or stopped.
            ///  
            internal bool Suspend()
            { 
                lock (_[....]) 
                {
                    if (this._timer != null) 
                    {
                        this._timer.Change(Timeout.Infinite, Timeout.Infinite);
                        return true;
                    } 
                    else
                    { 
                        return false; 
                    }
                } 
            }

            /// 
            /// Causes this eviction timer to generate periodic callbacks, provided it has not been permanently stopped (using the  method). 
            /// 
            ///  
            ///   Thread-safe. May be called regardless of the current state of the eviction timer. 
            /// 
            internal void Resume() 
            {
                lock (_[....])
                {
                    if (this._timer != null) 
                    {
                        this._timer.Change(this._period, this._period); 
                    } 
                }
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....], [....]
//----------------------------------------------------------------------------- 
 
namespace System.Data.Common.QueryCache
{ 
    using System;
    using System.Collections.Generic;
    using System.Data.EntityClient;
    using System.Data.Metadata.Edm; 
    using System.Data.Objects.Internal;
    using System.Diagnostics; 
    using System.Threading; 
    using System.Data.Common.Internal.Materialization;
 
    /// 
    /// Provides Query Execution Plan Caching Service
    /// 
    ///  
    /// Thread safe.
    /// Dispose must be called as there is no finalizer for this class 
    ///  
    internal class QueryCacheManager : IDisposable
    { 
        #region Constants/Default values for configuration parameters

        /// 
        /// Default Soft maximum number of entries in the cache 
        /// Default value: 1000
        ///  
        const int DefaultMaxNumberOfEntries = 1000; 

        ///  
        /// Default high mark for starting sweeping process
        /// default value: 80% of MaxNumberOfEntries
        /// 
        const float DefaultHighMarkPercentageFactor = 0.8f; // 80% of MaxLimit 

        ///  
        /// Recycler timer period 
        /// 
        const int DefaultRecyclerPeriodInMilliseconds = 60 * 1000; 

        #endregion

        #region Fields 

        ///  
        /// cache lock object 
        /// 
        private readonly object _cacheDataLock = new object(); 

        /// 
        /// cache data
        ///  
        private readonly Dictionary _cacheData = new Dictionary(32);
 
        ///  
        /// soft maximum number of entries in the cache
        ///  
        private readonly int _maxNumberOfEntries;

        /// 
        /// high mark of the number of entries to trigger the sweeping process 
        /// 
        private readonly int _sweepingTriggerHighMark; 
 
        /// 
        /// Eviction timer 
        /// 
        private readonly EvictionTimer _evictionTimer;

        #endregion 

        #region Construction and Initialization 
 
        /// 
        /// Constructs a new Query Cache Manager instance, with default values for all 'configurable' parameters. 
        /// 
        /// A new instance of  configured with default entry count, load factor and recycle period
        internal static QueryCacheManager Create()
        { 
            return new QueryCacheManager(DefaultMaxNumberOfEntries, DefaultHighMarkPercentageFactor, DefaultRecyclerPeriodInMilliseconds);
        } 
 
        /// 
        /// Cache Constructor 
        /// 
        /// 
        ///   Maximum number of entries that the cache should contain.
        ///  
        /// 
        ///   The number of entries that must be present, as a percentage, before entries should be removed 
        ///   according to the eviction policy. 
        ///   Must be greater than 0 and less than or equal to 1.0
        ///  
        /// 
        ///   The interval, in milliseconds, at which the number of entries will be compared to the load factor
        ///   and eviction carried out if necessary.
        ///  
        private QueryCacheManager(int maximumSize, float loadFactor, int recycleMillis)
        { 
            Debug.Assert(maximumSize > 0, "Maximum size must be greater than zero"); 
            Debug.Assert(loadFactor > 0 && loadFactor <= 1, "Load factor must be greater than 0.0 and less than or equal to 1.0");
            Debug.Assert(recycleMillis >= 0, "Recycle period in milliseconds must not be negative"); 

            //
            // Load hardcoded defaults
            // 
            this._maxNumberOfEntries = maximumSize;
 
            // 
            // set sweeping high mark trigger value
            // 
            this._sweepingTriggerHighMark = (int)(_maxNumberOfEntries * loadFactor);

            //
            // Initialize Recycler 
            //
            this._evictionTimer = new EvictionTimer(this, recycleMillis); 
        } 

        #endregion 

        #region 'External' interface
        /// 
        /// Adds new entry to the cache using "abstract" cache context and 
        /// value; returns an existing entry if the key is already in the
        /// dictionary. 
        ///  
        /// 
        ///  
        /// The existing entry in the dicitionary if already there;
        /// inQueryCacheEntry if none was found and inQueryCacheEntry
        /// was added instead.
        ///  
        /// true if the output entry was already found; false if it had to be added.
        internal bool TryLookupAndAdd(QueryCacheEntry inQueryCacheEntry, out QueryCacheEntry outQueryCacheEntry) 
        { 
            Debug.Assert(null != inQueryCacheEntry, "qEntry must not be null");
 
            outQueryCacheEntry = null;

            lock (_cacheDataLock)
            { 
                if (!_cacheData.TryGetValue(inQueryCacheEntry.QueryCacheKey, out outQueryCacheEntry))
                { 
                    // 
                    // add entry to cache data
                    // 
                    _cacheData.Add(inQueryCacheEntry.QueryCacheKey, inQueryCacheEntry);

                    return false;
                } 
                else
                { 
                    outQueryCacheEntry.QueryCacheKey.UpdateHit(); 

                    return true; 
                }
            }
        }
 
        /// 
        /// Lookup service for EntityClient cache key 
        ///  
        /// 
        ///  
        /// 
        internal bool TryCacheLookup(EntityClientCacheKey entityClientCacheKey, out EntityCommandDefinition entityCommandDefinition)
        {
            Debug.Assert(null != entityClientCacheKey, "entityClientCacheKey must not be null"); 

            entityCommandDefinition = null; 
 
            QueryCacheEntry qEntry = null;
 
            //
            // invoke internal lookup
            //
            bool bHit = TryInternalCacheLookup(entityClientCacheKey, out qEntry); 

            // 
            // if it is a hit, 'extract' the entry strong type cache value 
            //
            if (bHit) 
            {
                entityCommandDefinition = ((EntityClientCacheEntry)qEntry).GetTarget();
            }
 
            return bHit;
        } 
 
        /// 
        /// Lookup service for ShaperFactoryQueryCacheKey 
        /// 
        /// 
        /// 
        ///  
        /// 
        internal bool TryCacheLookup(ShaperFactoryQueryCacheKey key, out ShaperFactory factory) 
        { 
            Debug.Assert(null != key, "key null");
            QueryCacheEntry cacheEntry; 
            if (TryInternalCacheLookup(key, out cacheEntry))
            {
                factory = (ShaperFactory)cacheEntry.GetTarget();
                return true; 
            }
            factory = null; 
            return false; 
        }
 
        /// 
        /// Lookup service for Entity-SQL ObjectQuery cache key
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        internal bool TryCacheLookup(EntitySqlQueryCacheKey objectQueryCacheKey, out ObjectQueryExecutionPlan execPlan) 
        {
            Debug.Assert(null != objectQueryCacheKey, "objectQueryCacheKey must not be null");

            execPlan = null; 
            QueryCacheEntry qEntry = null;
 
            // 
            // invoke internal lookup
            // 
            bool bHit = TryInternalCacheLookup(objectQueryCacheKey, out qEntry);

            //
            // if it is a hit, 'extract' the entry strong type cache value 
            //
            if (bHit) 
            { 
                execPlan = ((EntitySqlQueryCacheEntry)qEntry).ExecutionPlan;
            } 

            return bHit;
        }
 
        /// 
        /// Lookup service for CompiledQuery cache key 
        ///  
        /// 
        ///  
        /// 
        /// 
        /// 
        internal bool TryCacheLookup(CompiledQueryCacheKey objectQueryCacheKey, out CompiledQueryCacheEntry cacheEntry) 
        {
            Debug.Assert(null != objectQueryCacheKey, "objectQueryCacheKey must not be null"); 
 
            cacheEntry = null;
            QueryCacheEntry qEntry = null; 

            //
            // invoke internal lookup
            // 
            bool bHit = TryInternalCacheLookup(objectQueryCacheKey, out qEntry);
 
            // 
            // if it is a hit, 'extract' the entry strong type cache value
            // 
            if (bHit)
            {
                cacheEntry = (CompiledQueryCacheEntry)qEntry;
            } 

            return bHit; 
        } 

        ///  
        /// Clears the Cache
        /// 
        internal void Clear()
        { 
            lock (_cacheDataLock)
            { 
                _cacheData.Clear(); 
            }
        } 
        #endregion

        #region Private Members
 
        /// 
        /// lookup service 
        ///  
        /// 
        ///  
        /// true if cache hit, false if cache miss
        private bool TryInternalCacheLookup( QueryCacheKey queryCacheKey, out QueryCacheEntry queryCacheEntry )
        {
            Debug.Assert(null != queryCacheKey, "queryCacheKey must not be null"); 

            queryCacheEntry = null; 
 
            bool bHit = false;
 
            //
            // lock the cache for the minimal possible period
            //
            lock (_cacheDataLock) 
            {
                bHit = _cacheData.TryGetValue(queryCacheKey, out queryCacheEntry); 
            } 

            // 
            // if cache hit
            //
            if (bHit)
            { 
                //
                // update hit mark in cache key 
                // 
                queryCacheEntry.QueryCacheKey.UpdateHit();
            } 

            return bHit;
        }
 

        ///  
        /// Recycler handler. This method is called directly by the eviction timer. 
        /// It should take no action beyond invoking the  method on the
        /// cache manager instance passed as . 
        /// 
        /// The cache manager instance on which the 'recycle' handler should be invoked
        private static void CacheRecyclerHandler(object state)
        { 
            ((QueryCacheManager)state).SweepCache();
        } 
 
        /// 
        /// Aging factor 
        /// 
        private static readonly int[] _agingFactor = {1,1,2,4,8,16};
        private static readonly int AgingMaxIndex = _agingFactor.Length - 1;
 
        /// 
        /// Sweeps the cache removing old unused entries. 
        /// This method implements the query cache eviction policy. 
        /// 
        private void SweepCache() 
        {
            //
            // Disable eviction timer
            // 
            if (!this._evictionTimer.Suspend())
            { 
                // Return value of false indicates the manager and timer have been disposed. 
                return;
            } 

            lock (_cacheDataLock)
            {
                // 
                // recycle only if entries exceeds the high mark factor
                // 
                if (_cacheData.Count > _sweepingTriggerHighMark) 
                {
                    // 
                    // sweep the cache
                    //
                    uint evictedEntriesCount = 0;
                    List cacheKeys = new List(_cacheData.Count); 
                    Debug.Assert(GC.GetGeneration(cacheKeys) < 2, "we should not allocate temp object in the LOH");
 
                    cacheKeys.AddRange(_cacheData.Keys); 
                    for (int i = 0; i < cacheKeys.Count; i++)
                    { 
                        //
                        // if entry was not used in the last time window, then evict the entry
                        //
                        if (0 == cacheKeys[i].HitCount) 
                        {
                            _cacheData.Remove(cacheKeys[i]); 
                            evictedEntriesCount++; 
                        }
                        // 
                        // otherwise, age the entry in a progressive scheme
                        //
                        else
                        { 
                            int agingIndex = unchecked(cacheKeys[i].AgingIndex + 1);
                            if (agingIndex > AgingMaxIndex) 
                            { 
                                agingIndex = AgingMaxIndex;
                            } 
                            cacheKeys[i].AgingIndex = agingIndex;
                            cacheKeys[i].HitCount = cacheKeys[i].HitCount >> _agingFactor[agingIndex];
                        }
                    } 
                }
            } 
 
            //
            // Enable eviction timer 
            //
            this._evictionTimer.Resume();
        }
 
        #endregion
 
        #region IDisposable Members 

        ///  
        /// Dispose instance
        /// 
        /// Dispose must be called as there are no finalizers for this class
        public void Dispose() 
        {
            if (this._evictionTimer.Stop()) 
            { 
                this.Clear();
            } 
        }

        #endregion
 
        /// 
        /// Periodically invokes cache cleanup logic on a specified  instance, 
        /// and allows this periodic callback to be suspended, resumed or stopped in a thread-safe way. 
        /// 
        private sealed class EvictionTimer 
        {
            /// Used to control multi-threaded accesses to this instance
            private readonly object _[....] = new object();
 
            /// The required interval between invocations of the cache cleanup logic
            private readonly int _period; 
 
            /// The underlying  that implements the periodic callback
            private Timer _timer; 

            internal EvictionTimer(QueryCacheManager cacheManager, int recyclePeriod)
            {
                this._period = recyclePeriod; 
                this._timer = new Timer(QueryCacheManager.CacheRecyclerHandler, cacheManager, recyclePeriod, recyclePeriod);
            } 
 
            /// 
            /// Permanently stops the eviction timer. 
            /// It will no longer generate periodic callbacks and further calls to , , or ,
            /// though thread-safe, will have no effect.
            /// 
            ///  
            ///   If this eviction timer has already been stopped (using the  method), returns false;
            ///   otherwise, returns true to indicate that the call successfully stopped and cleaned up the underlying timer instance. 
            ///  
            /// 
            ///   Thread safe. May be called regardless of the current state of the eviction timer. 
            ///   Once stopped, an eviction timer cannot be restarted with the  method.
            /// 
            internal bool Stop()
            { 
                lock (_[....])
                { 
                    if (this._timer != null) 
                    {
                        this._timer.Dispose(); 
                        this._timer = null;
                        return true;
                    }
                    else 
                    {
                        return false; 
                    } 
                }
            } 

            /// 
            /// Pauses the operation of the eviction timer.
            ///  
            /// 
            ///   If this eviction timer has already been stopped (using the  method), returns false; 
            ///   otherwise, returns true to indicate that the call successfully suspended the inderlying  
            ///   and no further periodic callbacks will be generated until the  method is called.
            ///  
            /// 
            ///   Thread-safe. May be called regardless of the current state of the eviction timer.
            ///   Once suspended, an eviction timer may be resumed or stopped.
            ///  
            internal bool Suspend()
            { 
                lock (_[....]) 
                {
                    if (this._timer != null) 
                    {
                        this._timer.Change(Timeout.Infinite, Timeout.Infinite);
                        return true;
                    } 
                    else
                    { 
                        return false; 
                    }
                } 
            }

            /// 
            /// Causes this eviction timer to generate periodic callbacks, provided it has not been permanently stopped (using the  method). 
            /// 
            ///  
            ///   Thread-safe. May be called regardless of the current state of the eviction timer. 
            /// 
            internal void Resume() 
            {
                lock (_[....])
                {
                    if (this._timer != null) 
                    {
                        this._timer.Change(this._period, this._period); 
                    } 
                }
            } 
        }
    }
}

// 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