private const int nrBags = 265; // based on 240 timeslices + 20 bags for ItemLimit + 5 bags empty buffer public LifespanManager(FluidCache <T> owner, TimeSpan minAge, TimeSpan maxAge, GetNow getNow) { this.owner = owner; int maxMS = Math.Min((int)maxAge.TotalMilliseconds, 12 * 60 * 60 * 1000); // max = 12 hours this.minAge = minAge; this.getNow = getNow; this.maxAge = TimeSpan.FromMilliseconds(maxMS); validatyCheckInterval = TimeSpan.FromMilliseconds(maxMS / 240.0); // max timeslice = 3 min bagItemLimit = this.owner.Capacity / 20; // max 5% of capacity per bag bags = new AgeBag <T> [nrBags]; for (int loop = nrBags - 1; loop >= 0; --loop) { bags[loop] = new AgeBag <T>(); } OpenCurrentBag(getNow(), 0); }
/// <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> private void CleanUp(DateTime now) { lock (syncObject) { int itemsAboveCapacity = Stats.Current - Stats.Capacity; AgeBag <T> bag = bags[OldestBagIndex]; while (!HasProcessedAllBags && (AlmostOutOfBags || BagNeedsCleaning(bag, itemsAboveCapacity, now))) { itemsAboveCapacity = CleanBag(bag, itemsAboveCapacity); ++OldestBagIndex; bag = bags[OldestBagIndex]; } ++CurrentBagIndex; OpenBag(CurrentBagIndex); EnsureIndexIsValid(); } }
/// <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 (this.currentBag != null) { this.currentBag.StopTime = now; } // open new age bag for next time slice currentBagIndex = bagNumber; AgeBag <T> currentBag = bags[currentBagIndex % nrBags]; currentBag.StartTime = now; currentBag.First = null; this.currentBag = currentBag; // reset counters for CheckValidity() nextValidityCheck = now.Add(validatyCheckInterval); itemsInCurrentBag = 0; } }
private bool BagNeedsCleaning(AgeBag <T> bag, int itemsAboveCapacity, DateTime now) { return(bag.HasExpired(maxAge, now) || (itemsAboveCapacity > 0 && bag.HasReachedMinimumAge(minAge, now))); }