コード例 #1
0
        internal void UtcUpdateCacheEntry(MemoryCacheEntry cacheEntry, DateTime utcExpires)
        {
            lock (this)
            {
                ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef;
                if (cacheEntry.ExpiresBucket != _bucket || entryRef.IsInvalid)
                {
                    return;
                }

                ExpiresEntry[] entries    = (_pages[(entryRef.PageIndex)]._entries);
                int            entryIndex = entryRef.Index;

                Dbg.Assert(cacheEntry == entries[entryIndex]._cacheEntry);

                RemoveCount(entries[entryIndex]._utcExpires);
                AddCount(utcExpires);

                entries[entryIndex]._utcExpires = utcExpires;

                cacheEntry.UtcAbsExp = utcExpires;

                Dbg.Validate("CacheValidateExpires", this);
                Dbg.Trace("CacheExpiresUpdate", "Updated item " + cacheEntry.Key + " in bucket " + _bucket);
            }
        }
コード例 #2
0
        private void RemoveFromCache(MemoryCacheEntry entry, CacheEntryRemovedReason reason, bool delayRelease = false)
        {
            // release outside of lock
            if (entry != null)
            {
                if (entry.InExpires())
                {
                    _expires.Remove(entry);
                }

                if (entry.InUsage())
                {
                    _usage.Remove(entry);
                }

                Dbg.Assert(entry.State == EntryState.RemovingFromCache, "entry.State = EntryState.RemovingFromCache");

                entry.State = EntryState.RemovedFromCache;
                if (!delayRelease)
                {
                    entry.Release(_cache, reason);
                }
                if (_perfCounters != null)
                {
                    _perfCounters.Decrement(PerfCounterName.Entries);
                    _perfCounters.Increment(PerfCounterName.Turnover);
                }
            }
        }
コード例 #3
0
        private void RemoveFromList(int pageIndex, ref ExpiresPageList list)
        {
            Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)");

            if ((_pages[(pageIndex)]._pagePrev) != -1)
            {
                Dbg.Assert((_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) == pageIndex, "PageNext(PagePrev(pageIndex)) == pageIndex");
                (_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) = (_pages[(pageIndex)]._pageNext);
            }
            else
            {
                Dbg.Assert(list._head == pageIndex, "list._head == pageIndex");
                list._head = (_pages[(pageIndex)]._pageNext);
            }

            if ((_pages[(pageIndex)]._pageNext) != -1)
            {
                Dbg.Assert((_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) == pageIndex, "PagePrev(PageNext(pageIndex)) == pageIndex");
                (_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) = (_pages[(pageIndex)]._pagePrev);
            }
            else
            {
                Dbg.Assert(list._tail == pageIndex, "list._tail == pageIndex");
                list._tail = (_pages[(pageIndex)]._pagePrev);
            }

            (_pages[(pageIndex)]._pagePrev) = -1;
            (_pages[(pageIndex)]._pageNext) = -1;
        }
 internal MemoryCacheEntryChangeMonitor(ReadOnlyCollection <String> keys, String regionName, MemoryCache cache)
 {
     Dbg.Assert(keys != null && keys.Count > 0, "keys != null && keys.Count > 0");
     _keys       = keys;
     _regionName = regionName;
     InitDisposableMembers(cache);
 }
コード例 #5
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        private void AddUsageEntryToFreeList(UsageEntryRef entryRef)
        {
            Dbg.Assert(entryRef.IsRef1, "entryRef.IsRef1");

            UsageEntry[] entries    = (_pages[(entryRef.PageIndex)]._entries);
            int          entryIndex = entryRef.Ref1Index;

            Dbg.Assert(entries[entryIndex]._cacheEntry == null, "entries[entryIndex]._cacheEntry == null");
            entries[entryIndex]._utcDate    = DateTime.MinValue;
            entries[entryIndex]._ref1._prev = UsageEntryRef.INVALID;
            entries[entryIndex]._ref2._next = UsageEntryRef.INVALID;
            entries[entryIndex]._ref2._prev = UsageEntryRef.INVALID;

            entries[entryIndex]._ref1._next = ((entries)[0]._ref1._next);
            ((entries)[0]._ref1._next)      = entryRef;

            _cEntriesInUse--;
            int pageIndex = entryRef.PageIndex;

            ((entries)[0]._cFree)++;
            if (((entries)[0]._cFree) == 1)
            {
                AddToListHead(pageIndex, ref _freeEntryList);
            }
            else if (((entries)[0]._cFree) == NUM_ENTRIES)
            {
                RemovePage(pageIndex);
            }
        }
コード例 #6
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        internal void Add(MemoryCacheEntry cacheEntry)
        {
            byte bucket = cacheEntry.UsageBucket;

            Dbg.Assert(bucket != 0xff, "bucket != 0xff");
            _buckets[bucket].AddCacheEntry(cacheEntry);
        }
コード例 #7
0
        internal ExpiresEntryRef(int pageIndex, int entryIndex)
        {
            Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex");
            Dbg.Assert((entryIndex & ENTRY_MASK) == entryIndex, "(entryIndex & ENTRY_MASK) == entryIndex");
            Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0");

            _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK));
        }
コード例 #8
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        internal UsageEntryRef(int pageIndex, int entryIndex)
        {
            Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex");
            Dbg.Assert((Math.Abs(entryIndex) & ENTRY_MASK) == (Math.Abs(entryIndex)), "(Math.Abs(entryIndex) & ENTRY_MASK) == Math.Abs(entryIndex)");
            Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0");

            _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK));
        }
コード例 #9
0
        private int RemoveFromListHead(ref ExpiresPageList list)
        {
            Dbg.Assert(list._head != -1, "list._head != -1");

            int oldHead = list._head;

            RemoveFromList(oldHead, ref list);
            return(oldHead);
        }
コード例 #10
0
        protected override int GetCurrentPressure()
        {
            // Call GetUpdatedTotalCacheSize to update the total
            // cache size, if there has been a recent Gen 2 Collection.
            // This update must happen, otherwise the CacheManager won't
            // know the total cache size.
            int          gen2Count = GC.CollectionCount(2);
            SRefMultiple sref      = _sizedRefMultiple;

            if (gen2Count != _gen2Count && sref != null)
            {
                // update _gen2Count
                _gen2Count = gen2Count;

                // the SizedRef is only updated after a Gen2 Collection

                // increment the index (it's either 1 or 0)
                Dbg.Assert(SAMPLE_COUNT == 2);
                _idx = _idx ^ 1;
                // remember the sample time
                _cacheSizeSampleTimes[_idx] = DateTime.UtcNow;
                // remember the sample value
                _cacheSizeSamples[_idx] = sref.ApproximateSize;
#if DBG
                Dbg.Trace("MemoryCacheStats", "SizedRef.ApproximateSize=" + _cacheSizeSamples[_idx]);
#endif
                IMemoryCacheManager memoryCacheManager = s_memoryCacheManager;
                if (memoryCacheManager != null)
                {
                    memoryCacheManager.UpdateCacheSize(_cacheSizeSamples[_idx], _memoryCache);
                }
            }

            // if there's no memory limit, then there's nothing more to do
            if (_memoryLimit <= 0)
            {
                return(0);
            }

            long cacheSize = _cacheSizeSamples[_idx];

            // use _memoryLimit as an upper bound so that pressure is a percentage (between 0 and 100, inclusive).
            if (cacheSize > _memoryLimit)
            {
                cacheSize = _memoryLimit;
            }

            // PerfCounter: Cache Percentage Process Memory Limit Used
            //    = memory used by this process / process memory limit at pressureHigh
            // Set private bytes used in kilobytes because the counter is a DWORD

            //


            int result = (int)(cacheSize * 100 / _memoryLimit);
            return(result);
        }
コード例 #11
0
ファイル: ChangeMonitor.cs プロジェクト: rsumner31/corefx2
        // Derived classes must call InitializationComplete
        protected void InitializationComplete()
        {
            _flags[INITIALIZED] = true;

            // If the dependency has already changed, or someone tried to dispose us, then call Dispose now.
            Dbg.Assert(_flags[INITIALIZED], "It is critical that INITIALIZED is set before CHANGED is checked below");
            if (_flags[CHANGED])
            {
                Dispose();
            }
        }
コード例 #12
0
ファイル: ChangeMonitor.cs プロジェクト: rsumner31/corefx2
        // Derived classes call OnChanged when the dependency changes.  Optionally,
        // they may pass state which will be passed to the OnChangedCallback.  The
        // OnChangedCallback is only invoked once, and only after NotifyOnChanged is
        // called by the cache implementer.  OnChanged is also invoked when the instance
        // is disposed, but only has an affect if the callback has not already been invoked.
        protected void OnChanged(Object state)
        {
            OnChangedHelper(state);

            // OnChanged will also invoke Dispose, but only after initialization is complete
            Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below.");
            if (_flags[INITIALIZED])
            {
                DisposeHelper();
            }
        }
コード例 #13
0
        bool IEqualityComparer.Equals(Object x, Object y)
        {
            Dbg.Assert(x != null && x is MemoryCacheKey);
            Dbg.Assert(y != null && y is MemoryCacheKey);

            MemoryCacheKey a, b;

            a = (MemoryCacheKey)x;
            b = (MemoryCacheKey)y;

            return(String.Compare(a.Key, b.Key, StringComparison.Ordinal) == 0);
        }
コード例 #14
0
        internal long TrimInternal(int percent)
        {
            Dbg.Assert(percent <= 100, "percent <= 100");

            int count  = Count;
            int toTrim = 0;

            // do we need to drop a percentage of entries?
            if (percent > 0)
            {
                toTrim = (int)Math.Ceiling(((long)count * (long)percent) / 100D);
                // would this leave us above MAX_COUNT?
                int minTrim = count - MAX_COUNT;
                if (toTrim < minTrim)
                {
                    toTrim = minTrim;
                }
            }
            // do we need to trim?
            if (toTrim <= 0 || _disposed == 1)
            {
                return(0);
            }
            int trimmed          = 0; // total number of entries trimmed
            int trimmedOrExpired = 0;

#if DEBUG
            int beginTotalCount = count;
#endif

            trimmedOrExpired = _expires.FlushExpiredItems(true);
            if (trimmedOrExpired < toTrim)
            {
                trimmed           = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired);
                trimmedOrExpired += trimmed;
            }

            if (trimmed > 0 && _perfCounters != null)
            {
                // Update values for perfcounters
                _perfCounters.IncrementBy(PerfCounterName.Trims, trimmed);
            }

#if DEBUG
            Dbg.Trace("MemoryCacheStore", "TrimInternal:"
                      + " beginTotalCount=" + beginTotalCount
                      + ", endTotalCount=" + count
                      + ", percent=" + percent
                      + ", trimmed=" + trimmed);
#endif
            return(trimmedOrExpired);
        }
コード例 #15
0
ファイル: ChangeMonitor.cs プロジェクト: rsumner31/corefx2
        public void Dispose()
        {
            OnChangedHelper(null);

            // If not initialized, throw, so the derived class understands that it must call InitializeComplete before Dispose.
            Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below.");
            if (!_flags[INITIALIZED])
            {
                throw new InvalidOperationException(SR.Init_not_complete);
            }

            DisposeHelper();
        }
コード例 #16
0
        internal CacheExpires(MemoryCacheStore cacheStore)
        {
            Dbg.Assert(NUMBUCKETS < Byte.MaxValue);

            DateTime utcNow = DateTime.UtcNow;

            _cacheStore = cacheStore;
            _buckets    = new ExpiresBucket[NUMBUCKETS];
            for (byte b = 0; b < _buckets.Length; b++)
            {
                _buckets[b] = new ExpiresBucket(this, b, utcNow);
            }
        }
コード例 #17
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        private void MoveToListHead(int pageIndex, ref UsagePageList list)
        {
            Dbg.Assert(list._head != -1, "list._head != -1");
            Dbg.Assert(list._tail != -1, "list._tail != -1");

            if (list._head == pageIndex)
            {
                return;
            }

            RemoveFromList(pageIndex, ref list);
            AddToListHead(pageIndex, ref list);
        }
コード例 #18
0
        private void MoveToListTail(int pageIndex, ref ExpiresPageList list)
        {
            Dbg.Assert(list._head != -1, "list._head != -1");
            Dbg.Assert(list._tail != -1, "list._tail != -1");

            if (list._tail == pageIndex)
            {
                return;
            }

            RemoveFromList(pageIndex, ref list);
            AddToListTail(pageIndex, ref list);
        }
コード例 #19
0
        private void InitZeroPages()
        {
            Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0");
            Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0");
            Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0");

            _pages               = null;
            _minEntriesInUse     = -1;
            _freePageList._head  = -1;
            _freePageList._tail  = -1;
            _freeEntryList._head = -1;
            _freeEntryList._tail = -1;
        }
コード例 #20
0
        protected void InitHistory()
        {
            Dbg.Assert(_pressureHigh > 0, "_pressureHigh > 0");
            Dbg.Assert(_pressureLow > 0, "_pressureLow > 0");
            Dbg.Assert(_pressureLow <= _pressureHigh, "_pressureLow <= _pressureHigh");

            int pressure = GetCurrentPressure();

            _pressureHist = new int[HISTORY_COUNT];
            for (int i = 0; i < HISTORY_COUNT; i++)
            {
                _pressureHist[i] = pressure;
                _pressureTotal  += pressure;
            }
        }
コード例 #21
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        private void InitZeroPages()
        {
            Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0");
            Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0");
            Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0");
            Dbg.Assert(_lastRefHead.IsInvalid, "_lastRefHead.IsInvalid");
            Dbg.Assert(_lastRefTail.IsInvalid, "_lastRefTail.IsInvalid");
            Dbg.Assert(_addRef2Head.IsInvalid, "_addRef2Head.IsInvalid");

            _pages               = null;
            _minEntriesInUse     = -1;
            _freePageList._head  = -1;
            _freePageList._tail  = -1;
            _freeEntryList._head = -1;
            _freeEntryList._tail = -1;
        }
コード例 #22
0
        internal PhysicalMemoryMonitor(int physicalMemoryLimitPercentage)
        {
            /*
             * The chart below shows physical memory in megabytes, and the 1, 3, and 10% values.
             * When we reach "middle" pressure, we begin trimming the cache.
             *
             * RAM     1%      3%      10%
             * -----------------------------
             * 128     1.28    3.84    12.8
             * 256     2.56    7.68    25.6
             * 512     5.12    15.36   51.2
             * 1024    10.24   30.72   102.4
             * 2048    20.48   61.44   204.8
             * 4096    40.96   122.88  409.6
             * 8192    81.92   245.76  819.2
             *
             */

            long memory = TotalPhysical;

            Dbg.Assert(memory != 0, "memory != 0");
            if (memory >= 0x100000000)
            {
                _pressureHigh = 99;
            }
            else if (memory >= 0x80000000)
            {
                _pressureHigh = 98;
            }
            else if (memory >= 0x40000000)
            {
                _pressureHigh = 97;
            }
            else if (memory >= 0x30000000)
            {
                _pressureHigh = 96;
            }
            else
            {
                _pressureHigh = 95;
            }

            _pressureLow = _pressureHigh - 9;

            SetLimit(physicalMemoryLimitPercentage);
            InitHistory();
        }
コード例 #23
0
        private ExpiresEntryRef GetFreeExpiresEntry()
        {
            Dbg.Assert(_freeEntryList._head >= 0, "_freeEntryList._head >= 0");
            int pageIndex = _freeEntryList._head;

            ExpiresEntry[] entries    = (_pages[(pageIndex)]._entries);
            int            entryIndex = ((entries)[0]._next).Index;

            ((entries)[0]._next) = entries[entryIndex]._next;
            ((entries)[0]._cFree)--;
            if (((entries)[0]._cFree) == 0)
            {
                Dbg.Assert(((entries)[0]._next).IsInvalid, "FreeEntryHead(entries).IsInvalid");
                RemoveFromList(pageIndex, ref _freeEntryList);
            }
            return(new ExpiresEntryRef(pageIndex, entryIndex));
        }
コード例 #24
0
ファイル: CacheUsage.cs プロジェクト: rsumner31/corefx2
        private void AddToListHead(int pageIndex, ref UsagePageList list)
        {
            Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)");

            (_pages[(pageIndex)]._pagePrev) = -1;
            (_pages[(pageIndex)]._pageNext) = list._head;
            if (list._head != -1)
            {
                Dbg.Assert((_pages[(list._head)]._pagePrev) == -1, "PagePrev(list._head) == -1");
                (_pages[(list._head)]._pagePrev) = pageIndex;
            }
            else
            {
                list._tail = pageIndex;
            }

            list._head = pageIndex;
        }
コード例 #25
0
        private void AddToListTail(int pageIndex, ref ExpiresPageList list)
        {
            Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)");

            (_pages[(pageIndex)]._pageNext) = -1;
            (_pages[(pageIndex)]._pagePrev) = list._tail;
            if (list._tail != -1)
            {
                Dbg.Assert((_pages[(list._tail)]._pageNext) == -1, "PageNext(list._tail) == -1");
                (_pages[(list._tail)]._pageNext) = pageIndex;
            }
            else
            {
                list._head = pageIndex;
            }

            list._tail = pageIndex;
        }
コード例 #26
0
        internal void AddCacheEntry(MemoryCacheEntry cacheEntry)
        {
            lock (this)
            {
                if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0)
                {
                    return;
                }

                ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef;
                Dbg.Assert((cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid, "(cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid");
                if (cacheEntry.ExpiresBucket != 0xff || !entryRef.IsInvalid)
                {
                    return;
                }

                if (_freeEntryList._head == -1)
                {
                    Expand();
                }

                ExpiresEntryRef freeRef = GetFreeExpiresEntry();
                Dbg.Assert(cacheEntry.ExpiresBucket == 0xff, "cacheEntry.ExpiresBucket == 0xff");
                Dbg.Assert(cacheEntry.ExpiresEntryRef.IsInvalid, "cacheEntry.ExpiresEntryRef.IsInvalid");
                cacheEntry.ExpiresBucket   = _bucket;
                cacheEntry.ExpiresEntryRef = freeRef;

                ExpiresEntry[] entries    = (_pages[(freeRef.PageIndex)]._entries);
                int            entryIndex = freeRef.Index;
                entries[entryIndex]._cacheEntry = cacheEntry;
                entries[entryIndex]._utcExpires = cacheEntry.UtcAbsExp;

                AddCount(cacheEntry.UtcAbsExp);

                _cEntriesInUse++;

                if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0)
                {
                    RemoveCacheEntryNoLock(cacheEntry);
                }
            }
        }
コード例 #27
0
        private void UpdateMinEntries()
        {
            if (_cPagesInUse <= 1)
            {
                _minEntriesInUse = -1;
            }
            else
            {
                int capacity = _cPagesInUse * NUM_ENTRIES;
                Dbg.Assert(capacity > 0, "capacity > 0");
                Dbg.Assert(MIN_LOAD_FACTOR < 1.0, "MIN_LOAD_FACTOR < 1.0");

                _minEntriesInUse = (int)(capacity * MIN_LOAD_FACTOR);

                if ((_minEntriesInUse - 1) > ((_cPagesInUse - 1) * NUM_ENTRIES))
                {
                    _minEntriesInUse = -1;
                }
            }
        }
コード例 #28
0
        private void RemovePage(int pageIndex)
        {
            Dbg.Assert((((_pages[(pageIndex)]._entries))[0]._cFree) == NUM_ENTRIES, "FreeEntryCount(EntriesI(pageIndex)) == NUM_ENTRIES");

            RemoveFromList(pageIndex, ref _freeEntryList);
            AddToListHead(pageIndex, ref _freePageList);

            Dbg.Assert((_pages[(pageIndex)]._entries) != null, "EntriesI(pageIndex) != null");
            (_pages[(pageIndex)]._entries) = null;

            _cPagesInUse--;
            if (_cPagesInUse == 0)
            {
                InitZeroPages();
            }
            else
            {
                UpdateMinEntries();
            }
        }
コード例 #29
0
ファイル: MemoryCacheEntry.cs プロジェクト: rsumner31/corefx2
        internal void Release(MemoryCache cache, CacheEntryRemovedReason reason)
        {
            State = EntryState.Closed;

            // Are there any cache entries that depend on this entry?
            // If so, we need to fire their dependencies.
            Dictionary <MemoryCacheEntryChangeMonitor, MemoryCacheEntryChangeMonitor> .KeyCollection deps = null;
            // clone the dependents
            lock (this)
            {
                if (_fields != null && _fields._dependents != null && _fields._dependents.Count > 0)
                {
                    deps = _fields._dependents.Keys;
                    // set to null so RemoveDependent does not attempt to access it, since we're not
                    // using a copy of the KeyCollection.
                    _fields._dependents = null;
                    Dbg.Assert(_fields._dependents == null, "_fields._dependents == null");
                }
            }
            if (deps != null)
            {
                foreach (MemoryCacheEntryChangeMonitor dependent in deps)
                {
                    if (dependent != null)
                    {
                        dependent.OnCacheEntryReleased();
                    }
                }
            }

            CallCacheEntryRemovedCallback(cache, reason);

            // Dispose any dependencies
            if (_fields != null && _fields._dependencies != null)
            {
                foreach (ChangeMonitor monitor in _fields._dependencies)
                {
                    monitor.Dispose();
                }
            }
        }
コード例 #30
0
        public void Dispose()
        {
            if (Interlocked.Exchange(ref _disposed, 1) == 0)
            {
                // disable CacheExpires timer
                _expires.EnableExpirationTimer(false);
                // build array list of entries
                ArrayList entries = new ArrayList(_entries.Count);
                lock (_entriesLock) {
                    foreach (DictionaryEntry e in _entries)
                    {
                        MemoryCacheEntry entry = e.Value as MemoryCacheEntry;
                        entries.Add(entry);
                    }
                    foreach (MemoryCacheEntry entry in entries)
                    {
                        MemoryCacheKey key = entry as MemoryCacheKey;
                        entry.State = EntryState.RemovingFromCache;
                        _entries.Remove(key);
                    }
                }
                // release entries outside of lock
                foreach (MemoryCacheEntry entry in entries)
                {
                    RemoveFromCache(entry, CacheEntryRemovedReason.CacheSpecificEviction);
                }

                // MemoryCacheStatistics has been disposed, and therefore nobody should be using
                // _insertBlock except for potential threads in WaitInsertBlock (which won't care if we call Close).
                Dbg.Assert(_useInsertBlock == false, "_useInsertBlock == false");
                _insertBlock.Close();


                // Don't need to call GC.SuppressFinalize(this) for sealed types without finalizers.
            }
        }