diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index 11d6b84c6..81fb9d5b6 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -157,7 +157,7 @@ namespace Server { if (Running) { - logger.Error($"Timer is returned while still running! {new StackTrace()}"); + logger.Error($"Timer is returned while still running!\n{new StackTrace()}"); return; } diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index 89c7d75bd..b0bf65003 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -28,11 +28,12 @@ namespace Server private const int _tickRatePowerOf2 = 3; private const int _tickRate = 1 << _tickRatePowerOf2; // 8ms - private static long _lastTickTurned = -1; - private static readonly Timer[][] _rings = new Timer[_ringLayers][]; private static readonly int[] _ringIndexes = new int[_ringLayers]; + private static long _lastTickTurned = -1; + private static bool _timerWheelExecuting; + public static void Init(long tickCount) { _lastTickTurned = tickCount; @@ -61,29 +62,52 @@ namespace Server private static bool Turn() { bool events = false; + _timerWheelExecuting = true; + var turnNextWheel = false; + + var _executingRings = new Timer[_ringLayers]; + + // Detach the chain from the timer wheel. This allows adding timers to the same slot during execution. + for (var i = 0; i < _ringLayers; i++) + { + if (i == 0 || turnNextWheel) + { + var ringIndex = ++_ringIndexes[i]; + turnNextWheel = ringIndex >= _ringSize; + + if (turnNextWheel) + { + ringIndex = _ringIndexes[i] = 0; + } + + _executingRings[i] = _rings[i][ringIndex]; + _rings[i][ringIndex] = null; + } + else + { + _executingRings[i] = null; + } + } for (var i = 0; i < _ringLayers; i++) { - // Increment the ring index, then get the timer. - var ringIndex = ++_ringIndexes[i]; - bool turnNextWheel = ringIndex >= _ringSize; - - if (turnNextWheel) + var timer = _executingRings[i]; + if (timer == null) { - ringIndex = _ringIndexes[i] = 0; + continue; } - var timer = _rings[i][ringIndex]; - if (timer != null) + events = true; + + do { - events = true; + var next = timer._nextTimer; - do + timer.Detach(); + + // Check to see if it's running just in case it was stopped by another timer + if (timer.Running) { - var next = _rings[i][ringIndex] = timer._nextTimer; - - timer.Detach(); - if (i > 0 && timer._remaining > 0) { // Promote @@ -93,17 +117,14 @@ namespace Server { Execute(timer); } + } - timer = next; - } while (timer != null); - } - - if (!turnNextWheel) - { - break; - } + timer = next; + } while (timer != null); } + _timerWheelExecuting = false; + return events; } diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index 7d10bf441..176cf5748 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -102,13 +102,18 @@ namespace Server return; } - // We are at the head - if (_rings[_ring][_slot] == this) + // Do not detach if we are in the middle of executing the timer wheel for this ring/slot + if (!_timerWheelExecuting || _ringIndexes[_ring] != _slot) { - _rings[_ring][_slot] = _nextTimer; + // We are at the head + if (_rings[_ring][_slot] == this) + { + _rings[_ring][_slot] = _nextTimer; + } + + Detach(); } - Detach(); Running = false; Version++; var prof = GetProfile();