fix: Fixes timer wheel edge cases (#782)
1. Stopping a different timer, on the same slot as the timer being executed during OnTick
* Adds a check for the timer wheel currently being executed and does not detach the timer on Stop().
1. Timers are added to the current slot if they need 4095 slots, before the chain is fully detached/executed
* Removes all chains that will be executed from the timer wheel before executing.
This commit is contained in:
parent
bba5346a92
commit
7785f7e06c
3 changed files with 55 additions and 29 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue