/// <summary>Create item enumerator</summary> public IEnumerator <Node> GetEnumerator() { lock (this) { for (int bagNumber = _current; bagNumber >= _oldest; --bagNumber) { AgeBag bag = _bags[bagNumber]; // if bag.first == null then bag is empty or being cleaned up, so skip it! for (Node node = bag.First; node != null && bag.First != null; node = node.Next) { if (node.Value != null) { yield return(node); } } } } }
/// <summary>remove old items or items beyond capacity from LifespanMgr allowing them to be garbage collected</summary> /// <remarks>since we do not physically move items when touched we must check items in bag to determine if they should be deleted /// or moved. Also items that were removed by setting value to null get removed now. Rremoving an item from LifespanMgr allows /// it to be garbage collected. If removed item is retrieved by index prior to GC then it will be readded to LifespanMgr.</remarks> public void CleanUp(DateTime now) { lock (this) { //calculate how many items should be removed DateTime maxAge = now.Subtract(_maxAge); DateTime minAge = now.Subtract(_minAge); int itemsToRemove = _owner._curCount - _owner._capacity; AgeBag bag = _bags[_oldest % _size]; while (_current != _oldest && (_current - _oldest > _size - 5 || bag.startTime < maxAge || (itemsToRemove > 0 && bag.stopTime > minAge))) { // cache is still too big / old so remove oldest bag Node node = bag.first; bag.first = null; while (node != null) { Node next = node.next; node.next = null; if (node.Value != null && node.ageBag != null) { if (node.ageBag == bag) { // item has not been touched since bag was closed, so remove it from LifespanMgr ++itemsToRemove; node.ageBag = null; Interlocked.Decrement(ref _owner._curCount); } else { // item has been touched and should be moved to correct age bag now node.next = node.ageBag.first; node.ageBag.first = node; } } node = next; } // increment oldest bag bag = _bags[(++_oldest) % _size]; } OpenCurrentBag(now, ++_current); CheckIndexValid(); } }
/// <summary>ready a new current AgeBag for use and close the previous one</summary> private void OpenCurrentBag(DateTime now, int bagNumber) { lock (this) { // close last age bag if (_currentBag != null) { _currentBag.stopTime = now; } // open new age bag for next time slice AgeBag currentBag = _bags[(_current = bagNumber) % _size]; currentBag.startTime = now; currentBag.first = null; _currentBag = currentBag; // reset counters for CheckValid() _nextValidCheck = now.Add(_timeSlice); _currentSize = 0; } }
/// <summary>Updates the status of the node to prevent it from being dropped from cache</summary> public void Touch() { if (Value != null && ageBag != _mgr._currentBag) { if (ageBag == null) { lock (_mgr) if (ageBag == null) { // if node.AgeBag==null then the object is not currently managed by LifespanMgr so add it next = _mgr._currentBag.first; _mgr._currentBag.first = this; Interlocked.Increment(ref _mgr._owner._curCount); } } ageBag = _mgr._currentBag; Interlocked.Increment(ref _mgr._currentSize); } _mgr.CheckValid(); }