private void ClearCacheIfNeeded() { long Timestamp = PerformanceCounter.ElapsedMilliseconds; int Count = 0; while (Count++ < MaxRemovalsPerRun) { LinkedListNode <long> Node = SortedCache.First; if (Node == null) { break; } CacheBucket Bucket = Cache[Node.Value]; long TimeDelta = Timestamp - Bucket.Timestamp; if (TimeDelta <= MinTimeDelta && !UnderMemoryPressure()) { break; } SortedCache.Remove(Node); Cache.Remove(Node.Value); DeleteValueCallback(Bucket.Value); TotalSize -= Bucket.DataSize; } }
private void ClearCacheIfNeeded() { int Timestamp = Environment.TickCount; int Count = 0; while (Count++ < MaxRemovalsPerRun) { LinkedListNode <long> Node = SortedCache.First; if (Node == null) { break; } CacheBucket Bucket = Cache[Node.Value]; int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); if ((uint)TimeDelta <= (uint)MaxTimeDelta) { break; } SortedCache.Remove(Node); Cache.Remove(Node.Value); DeleteValueCallback(Bucket.Value); } }
private void ClearCacheIfNeeded() { int Timestamp = Environment.TickCount; while (TotalSize > MaxTotalSize) { lock (SortedCache) { LinkedListNode <long> Node = SortedCache.First; if (Node == null) { break; } CacheBucket Bucket = Cache[Node.Value]; int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); if ((uint)TimeDelta <= (uint)MaxTimeDelta) { break; } if (Cache.TryRemove(Node.Value, out Bucket)) { TotalSize -= Bucket.Size; SortedCache.Remove(Bucket.Node); } } } }
public void AddOrUpdate(long Key, T Value, long Size) { if (!Locked) { ClearCacheIfNeeded(); } LinkedListNode <long> Node = SortedCache.AddLast(Key); CacheBucket NewBucket = new CacheBucket(Value, Size, Node); if (Cache.TryGetValue(Key, out CacheBucket Bucket)) { if (Locked) { DeletePending.Enqueue(Bucket.Value); } else { DeleteValueCallback(Bucket.Value); } SortedCache.Remove(Bucket.Node); Cache[Key] = NewBucket; } else { Cache.Add(Key, NewBucket); } }
private void ClearCacheIfNeeded() { long Timestamp = PerformanceCounter.ElapsedMilliseconds; int Count = 0; while (Count++ < MaxRemovalsPerRun) { LinkedListNode <long> Node = SortedCache.First; if (Node == null) { break; } CacheBucket Bucket = Cache[Node.Value]; long TimeDelta = Timestamp - Bucket.Timestamp; if ((uint)TimeDelta <= (uint)MaxTimeDelta) { break; } SortedCache.Remove(Node); Cache.Remove(Node.Value); DeleteValueCallback(Bucket.Value); } }
private void ClearCacheIfNeeded() { long timestamp = PerformanceCounter.ElapsedMilliseconds; int count = 0; while (count++ < MaxRemovalsPerRun) { LinkedListNode <long> node = _sortedCache.First; if (node == null) { break; } CacheBucket bucket = _cache[node.Value]; long timeDelta = timestamp - bucket.Timestamp; if (timeDelta <= MinTimeDelta && !UnderMemoryPressure()) { break; } _sortedCache.Remove(node); _cache.Remove(node.Value); _deleteValueCallback(bucket.Value); _totalSize -= bucket.DataSize; } }
public void AddOrUpdate(long key, T value, long size) { if (!_locked) { ClearCacheIfNeeded(); } LinkedListNode <long> node = _sortedCache.AddLast(key); CacheBucket newBucket = new CacheBucket(value, size, node); if (_cache.TryGetValue(key, out CacheBucket bucket)) { if (_locked) { _deletePending.Enqueue(bucket.Value); } else { _deleteValueCallback(bucket.Value); } _sortedCache.Remove(bucket.Node); _totalSize -= bucket.DataSize; _cache[key] = newBucket; } else { _cache.Add(key, newBucket); } _totalSize += size; }
private void ClearCacheIfNeeded() { long timestamp = GetTimestamp(); while (_totalSize > MaxTotalSize) { lock (_sortedCache) { LinkedListNode <long> node = _sortedCache.First; if (node == null) { break; } CacheBucket bucket = _cache[node.Value]; long timeDelta = timestamp - bucket.Timestamp; if (timeDelta <= MinTimeDelta) { break; } if (_cache.TryRemove(node.Value, out bucket)) { _totalSize -= bucket.Size; _sortedCache.Remove(bucket.Node); } } } }
public void should_return_value_in_other_class() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var testValue = "B"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); var bucket2 = new CacheBucket(BUCKET_NAME_1, storage); bucket.SetValue(testKey, testValue); // Asserts bucket2.GetValue(testKey).Should().Be(testValue); }
public void should_create_bucket_success() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var testValue = "B"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); bucket.SetValue(testKey, testValue); // Asserts bucket.Name.Should().Be(BUCKET_NAME_1); bucket.GetValue(testKey).Should().Be(testValue); }
public CacheBucket Create(string name) { var names = BucketNameHelper.ExtractBucketNames(name); var rootName = names[0]; var storageType = CacheStorageMapping.Instance.GetStorageType(rootName); var storage = _sp.GetService(storageType) as ICacheStorage; var parent = GetParentBucket(name, storage, out var lastName); var bucket = new CacheBucket(lastName, storage) { Parent = parent }; return(bucket); }
public bool TryGetValue(long Key, out T Value) { if (Cache.TryGetValue(Key, out CacheBucket Bucket)) { Value = Bucket.Value; SortedCache.Remove(Bucket.Node); LinkedListNode <long> Node = SortedCache.AddLast(Key); Cache[Key] = new CacheBucket(Value, Bucket.DataSize, Node); return(true); } Value = default(T); return(false); }
public bool TryGetValue(long key, out T value) { if (_cache.TryGetValue(key, out CacheBucket bucket)) { value = bucket.Value; _sortedCache.Remove(bucket.Node); LinkedListNode <long> node = _sortedCache.AddLast(key); _cache[key] = new CacheBucket(value, bucket.DataSize, node); return(true); } value = default(T); return(false); }
public void should_return_correct_values() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var childContainer = "1"; var testValue = "B"; var testValue2 = "C"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); bucket.SetValue(testKey, testValue); bucket.In(childContainer).SetValue(testKey, testValue2); // Asserts testValue.Should().NotBe(testValue2); bucket.GetValue(testKey).Should().Be(testValue); bucket.In(childContainer).GetValue(testKey).Should().Be(testValue2); }
public void should_return_correct_value_when_quick_access() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var childContainer = "1"; var testValue2 = "C"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); bucket.In(childContainer).SetValue(testKey, testValue2); var childBucket = new CacheBucket(new[] { BUCKET_NAME_1, childContainer }.ToBucketName(), storage); // Asserts bucket.In(childContainer).GetValue(testKey).Should().Be(testValue2); childBucket.GetValue(testKey).Should().Be(testValue2); }
public void Should_consitent_value_between_2_instances() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var testValue = "B"; var testValue2 = "C"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); var bucket2 = new CacheBucket(BUCKET_NAME_1, storage); bucket.SetValue(testKey, testValue); bucket2.SetValue(testKey, testValue2); // Asserts bucket.GetValue(testKey) .Should().Be(bucket2.GetValue(testKey)) .And.Subject.Should().Be(testValue2); }
public void should_return_different_value_from_different_buckets() { // Arrange var storage = new InMemoryCacheStorage(); var testKey = "A"; var testValue = "B"; var testValue2 = "C"; // Actions var bucket = new CacheBucket(BUCKET_NAME_1, storage); var bucket2 = new CacheBucket(BUCKET_NAME_2, storage); bucket.SetValue(testKey, testValue); bucket2.SetValue(testKey, testValue2); // Asserts testValue.Should().NotBe(testValue2); bucket.GetValue(testKey).Should().Be(testValue); bucket2.GetValue(testKey).Should().Be(testValue2); }
private CacheBucket GetParentBucket(string name, ICacheStorage storage, out string lastName) { var bucketNames = BucketNameHelper.ExtractBucketNames(name); lastName = bucketNames.Last(); if (bucketNames.Length <= 1) { return(null); } CacheBucket parent = null; foreach (var bucketName in bucketNames.Skip(0).Take(bucketNames.Length - 1).Reverse()) { var bucket = new CacheBucket(bucketName, storage) { Parent = parent }; parent = bucket; } return(parent); }
public void AddOrUpdate(long Position, ATranslatedSub Subroutine, int Size) { ClearCacheIfNeeded(); TotalSize += Size; lock (SortedCache) { LinkedListNode <long> Node = SortedCache.AddLast(Position); CacheBucket NewBucket = new CacheBucket(Subroutine, Node, Size); Cache.AddOrUpdate(Position, NewBucket, (Key, Bucket) => { TotalSize -= Bucket.Size; SortedCache.Remove(Bucket.Node); return(NewBucket); }); } }
public void AddOrUpdate(long position, TranslatedSub subroutine, int size) { ClearCacheIfNeeded(); lock (_sortedCache) { _totalSize += size; LinkedListNode <long> node = _sortedCache.AddLast(position); CacheBucket newBucket = new CacheBucket(subroutine, node, size); _cache.AddOrUpdate(position, newBucket, (key, bucket) => { _totalSize -= bucket.Size; _sortedCache.Remove(bucket.Node); return(newBucket); }); } }
public TranslatedSub GetOrAdd(long position, TranslatedSub subroutine, int size) { ClearCacheIfNeeded(); lock (_sortedCache) { LinkedListNode <long> node = _sortedCache.AddLast(position); CacheBucket bucket = new CacheBucket(subroutine, node, size); bucket = _cache.GetOrAdd(position, bucket); if (bucket.Node == node) { _totalSize += size; } else { _sortedCache.Remove(node); } return(bucket.Subroutine); } }
public Enumerator(CacheBucket newBucket) { bucket = newBucket; idx = -1; }
/// <summary> /// The time thread loop /// </summary> private void TimeThread() { log.InfoFormat("started timer thread {0} (ID:{1})", m_name, Thread.CurrentThread.ManagedThreadId); int timeBalance = 0; uint workStart, workEnd; GameTimer chain, next, bucketTimer; workStart = workEnd = (uint)GetTickCount(); #if MonitorCallbacks Timer t = new Timer(new TimerCallback(SlowTimerCallback), null, Timeout.Infinite, Timeout.Infinite); #endif while (m_running) { try { // fire timers lock (m_buckets) { if (DOL.GS.GameEvents.RegionTimersResynch.watch != null) { m_time = DOL.GS.GameEvents.RegionTimersResynch.watch.ElapsedMilliseconds; } else { m_time++; } int newTick = m_tick = (m_tick + 1) & TICK_MASK; if ((newTick & BUCKET_MASK) == 0) { // cache next bucket int index = newTick >> BUCKET_BITS; next = m_buckets[index]; if (next != null) { m_buckets[index] = null; // sort the new cached bucket do { GameTimer timer = next; next = next.m_nextTimer; long index2 = timer.m_tick; if ((index2 & LONGTERM_MASK) != 0 && ((index2 -= 1 << TABLE_BITS + BUCKET_BITS) & LONGTERM_MASK) != 0) { // reinsert longterm timers back timer.m_tick = index2; bucketTimer = m_buckets[index]; m_buckets[index] = timer; } else { timer.m_tick = index2; index2 &= CACHE_MASK; bucketTimer = m_cachedBucket[index2].FirstTimer; m_cachedBucket[index2].FirstTimer = timer; if (m_cachedBucket[index2].LastTimer == null) { m_cachedBucket[index2].LastTimer = timer; } } if (bucketTimer == null) { timer.m_nextTimer = null; } else { timer.m_nextTimer = bucketTimer; } }while (next != null); } } int cacheIndex = m_tick & CACHE_MASK; chain = m_cachedBucket[cacheIndex].FirstTimer; if (chain != null) { m_cachedBucket[cacheIndex] = CacheBucket.EmptyBucket; } } GameTimer current = chain; int curTick = m_tick; int currentBucketMax = (curTick & ~BUCKET_MASK) + BUCKET_MASK; while (current != null) { if (current.m_tick == curTick) { try { long callbackStart = GetTickCount(); #if MonitorCallbacks m_currentTimer = current; m_timerTickStart = callbackStart; t.Change(200, 100); #endif current.OnTick(); #if MonitorCallbacks m_currentTimer = null; t.Change(Timeout.Infinite, Timeout.Infinite); if (GetTickCount() - callbackStart > 200) { lock (m_delayLog) { m_delayLog.Write("\n========================================================\n\n"); } } #endif if (GetTickCount() - callbackStart > 250) { if (log.IsWarnEnabled) { string curStr; try { curStr = current.ToString(); } catch (Exception ee) { curStr = "error in timer.ToString(): " + current.GetType().FullName + "; " + ee.ToString(); } string warning = "callback took " + (GetTickCount() - callbackStart) + "ms! " + curStr; log.Warn(warning); } } #if CollectStatistic // statistic int start = GetTickCount(); string callback; if (current is RegionTimer) { callback = ((RegionTimer)current).Callback.Method.ToString(); } else { callback = current.GetType().FullName; } lock (m_timerCallbackStatistic) { object obj = m_timerCallbackStatistic[callback]; if (obj == null) { m_timerCallbackStatistic[callback] = 1; } else { m_timerCallbackStatistic[callback] = ((int)obj) + 1; } } if (GetTickCount() - start > 500) { if (log.IsWarnEnabled) { log.Warn("Ticker statistic " + callback + " took more than 500ms!"); } } #endif } catch (Exception e) { string curStr; try { curStr = current.ToString(); } catch (Exception ee) { curStr = "error in timer.ToString(): " + current.GetType().FullName + "; " + ee.ToString(); } if (log.IsErrorEnabled) { log.Error("Timer callback (" + curStr + ")", e); } current.m_tick = TIMER_DISABLED | TIMER_RESCHEDULED; } m_invokedCount++; } else if ((current.m_tick & TIMER_RESCHEDULED) == 0) { // log.ErrorFormat("timer tick != current tick (0x{0:X4}), fired anyway: {1}", curTick, current); try { current.OnTick(); } catch (Exception e) { log.Error("timer error", e); current.m_tick = TIMER_DISABLED | TIMER_RESCHEDULED; } } lock (m_buckets) { next = current.m_nextTimer; long tick = current.m_tick; long interval = current.m_interval; if ((tick & TIMER_DISABLED) != 0 || (interval == 0 && (tick & TIMER_RESCHEDULED) == 0)) { m_activeTimers--; current.m_nextTimer = null; current.m_tick = TIMER_DISABLED; current.m_targetTime = -1; } else { ///// REINSERT all including rescheduled timers if ((tick & TIMER_RESCHEDULED) != 0) { current.m_tick = tick &= ~TIMER_RESCHEDULED; } else { current.m_targetTime = (int)(CurrentTime + interval); current.m_tick = tick = curTick + interval; } if (tick - curTick <= CACHE_MASK + 1) { tick &= CACHE_MASK; current.m_tick &= TICK_MASK; CacheBucket bucket = m_cachedBucket[tick]; GameTimer prev = bucket.LastTimer; current.m_nextTimer = null; if (prev != null) { prev.m_nextTimer = current; bucket.LastTimer = current; } else { bucket.FirstTimer = current; bucket.LastTimer = current; } m_cachedBucket[tick] = bucket; } else { if ((tick & TICK_MASK) > currentBucketMax) { current.m_tick = tick += TICK_MASK + 1; // extra pass if the timer is ahead of current tick } tick = (tick >> BUCKET_BITS) & TABLE_MASK; bucketTimer = m_buckets[tick]; if (bucketTimer == null) { current.m_nextTimer = null; } else { current.m_nextTimer = bucketTimer; } m_buckets[tick] = current; } ///// } } current = next; } bucketTimer = null; workEnd = (uint)GetTickCount(); timeBalance += 1 - (int)(workEnd - workStart); if (timeBalance > 0) { Thread.Sleep(timeBalance); workStart = (uint)GetTickCount(); timeBalance -= (int)(workStart - workEnd); } else { if (timeBalance < -1000) { // We can not increase forever if we get out of // sync. At some point we have to print out a warning // and catch up some time! if (log.IsWarnEnabled && timeBalance < -2000) { // Again, too much warning spam is meaningless. Will warn if time sync is more than the typical 1 to 2 seconds // -tolakram log.Warn(Name + " out of sync, over 2000ms lost! " + timeBalance.ToString()); } timeBalance += 1000; } workStart = workEnd; } } catch (ThreadAbortException e) { if (log.IsWarnEnabled) { log.Warn("Time manager thread \"" + m_name + "\" was aborted", e); } m_running = false; break; } catch (Exception e) { if (log.IsErrorEnabled) { log.Error("Exception in time manager \"" + m_name + "\"!", e); } } } log.InfoFormat("stopped timer thread {0} (ID:{1})", m_name, Thread.CurrentThread.ManagedThreadId); }
/// <summary> /// Removes the timer from the table without locking the table /// </summary> /// <param name="timer">The timer to remove</param> private void RemoveTimerUnsafe(GameTimer timer) { GameTimer t = timer; long tick = t.m_tick; if ((tick & TIMER_DISABLED) != 0) { return; } timer.m_targetTime = -1; // never change the active chain if (tick == m_tick || (tick & TIMER_RESCHEDULED) != 0) { t.m_tick = TIMER_DISABLED | TIMER_RESCHEDULED; return; } m_activeTimers--; // check the cache first long cachedIndex = tick & CACHE_MASK; CacheBucket bucket = m_cachedBucket[cachedIndex]; if (bucket.FirstTimer == t) { t.m_tick = TIMER_DISABLED; bucket.FirstTimer = t.m_nextTimer; if (bucket.LastTimer == t) { bucket.LastTimer = t.m_nextTimer; } t.m_nextTimer = null; m_cachedBucket[cachedIndex] = bucket; return; } GameTimer timerChain = bucket.FirstTimer; GameTimer prev; while (timerChain != null) { prev = timerChain; timerChain = timerChain.m_nextTimer; if (timerChain == t) { prev.m_nextTimer = t.m_nextTimer; t.m_nextTimer = null; t.m_tick = TIMER_DISABLED; if (bucket.LastTimer == t) { bucket.LastTimer = prev; m_cachedBucket[cachedIndex] = bucket; } return; } } // check the buckets tick = (tick >> BUCKET_BITS) & TABLE_MASK; timerChain = m_buckets[tick]; if (timerChain == t) { timerChain = timerChain.m_nextTimer; m_buckets[tick] = timerChain; t.m_nextTimer = null; t.m_tick = TIMER_DISABLED; return; } while (timerChain != null) { prev = timerChain; timerChain = timerChain.m_nextTimer; if (timerChain == t) { prev.m_nextTimer = t.m_nextTimer; break; } } t.m_nextTimer = null; t.m_tick = TIMER_DISABLED; }
/// <summary> /// Inserts the timer into the table. /// </summary> /// <param name="t">The timer to insert</param> /// <param name="offsetTick">The offset from current tick. min value=1, max value<MaxInterval</param> internal void InsertTimer(GameTimer t, int offsetTick) { if (offsetTick > MaxInterval || offsetTick < 1) { throw new ArgumentOutOfRangeException("offsetTick", offsetTick.ToString(), "Offset must be in range from 1 to " + MaxInterval); } GameTimer timer = t; lock (m_buckets) { long timerTick = timer.m_tick; long targetTick = m_tick + offsetTick; if (timerTick == m_tick || (timerTick & TIMER_RESCHEDULED) != 0) { timer.m_targetTime = (int)(CurrentTime + offsetTick); timer.m_tick = targetTick | TIMER_RESCHEDULED; return; } if ((timerTick & TIMER_DISABLED) == 0) { RemoveTimerUnsafe(timer); } timer.m_targetTime = (int)(CurrentTime + offsetTick); m_activeTimers++; if (offsetTick <= CACHE_MASK + 1) { timer.m_tick = targetTick & TICK_MASK; targetTick &= CACHE_MASK; CacheBucket bucket = m_cachedBucket[targetTick]; GameTimer prev = bucket.LastTimer; if (prev != null) { prev.m_nextTimer = timer; m_cachedBucket[targetTick].LastTimer = timer; } else { bucket.FirstTimer = timer; bucket.LastTimer = timer; m_cachedBucket[targetTick] = bucket; } } else { if ((targetTick & TICK_MASK) > (m_tick & ~BUCKET_MASK) + BUCKET_MASK) { targetTick += TICK_MASK + 1; // extra pass if the timer is ahead of current tick } timer.m_tick = targetTick; targetTick = (targetTick >> BUCKET_BITS) & TABLE_MASK; GameTimer next = m_buckets[targetTick]; m_buckets[targetTick] = timer; if (next != null) { timer.m_nextTimer = next; } } } }