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