Example #1
0
        public void AddOrAccess(T item, IWeakAction <T> evictor)
        {
            Contract.ThrowIfNull(evictor);

            // move up cost upper bound by fixed increase amount.
            Interlocked.Add(ref this.currentCostUpperBound, this.fixedIncrementAmount);

            // check whether it already exist. we do this here since we don't want to
            // create new CacheEntry unnecessarily
            CacheEntry existingEntry;
            var        existed = this.items.TryGetValue(item, out existingEntry);

            // cache miss
            if (!existed)
            {
                // try add - could have been added by other threads
                var newCacheEntry = new CacheEntry(CacheId.NextItemId(), evictor);
                existingEntry = this.items.AddOrUpdate(item, newCacheEntry, updateCacheEntry);

                // it was already cached by other thread
                existed = newCacheEntry != existingEntry;
            }
            else
            {
                // there is a race here. entry might have been kicked out from the cache and we are changing a
                // stale one. the race could introduce a situation where an item that has been chosen for eviction by one thread
                // is being accessed here. that's unfortunate, but I feel it's acceptable.
                UpdateCacheEntry(item, existingEntry);
            }

            CreateEvictionTaskIfNecessary();

            Logger.Log(FeatureId.Cache, FunctionId.Cache_AddOrAccess, logAddOrAccessed, existingEntry.ItemId, item, existed);
        }
Example #2
0
        protected CostBasedCache(
            IOptionService optionService,
            int minCount, long minCost, long maxCost,
            double coolingRate, int fixedIncAmount, TimeSpan dataCollectionTimeSpan,
            Func <T, long> costCalculator,
            bool eagarlyEvict,
            Func <T, string> uniqueIdGetter)
        {
            // set cache id for this cache
            this.cacheId = CacheId.NextCacheId();

            // log all variables used to create the cache. this should be called only once. should be okay to allocate the string.
            Logger.Log(FunctionId.Cache_Created, string.Join(",", cacheId, typeof(T), minCount, minCost, maxCost, coolingRate, fixedIncAmount, dataCollectionTimeSpan.TotalMilliseconds));

            this.minCount = minCount;
            this.minCost  = minCost;
            this.maxCost  = maxCost;

            this.fixedIncrementAmount = fixedIncAmount;
            this.coolingRate          = coolingRate;

            this.dataCollectionBufferInMS = (int)dataCollectionTimeSpan.TotalMilliseconds;

            this.costCalculator = costCalculator;
            this.uniqueIdGetter = uniqueIdGetter;

            this.eagarlyEvict = eagarlyEvict;

            this.updateCacheEntry    = UpdateCacheEntry;
            this.logAddOrAccessed    = LogAddOrAccessed;
            this.logEvictedOrRemoved = LogEvictedOrRemoved;

            this.comparer = Comparer;

            // use environment tick here which is known to be the least expensive one to get and low
            // but good enough accuracy for the cache (15ms on most of system)
            this.lastTimeTaskRan = Environment.TickCount;

            // set base cost
            this.currentCostUpperBound = minCost;

            this.rankGetter = RankingAlgorithm;

            // respond to option change
            this.optionService = optionService;
            this.optionService.OptionChanged += OnOptionChanged;
        }