fix: Fixes timer orphaning issue (#1196)
* Fixes an edge case where a check to see if a timer was being executed resulted in it not being properly detached on stop. Fixed this by changing how we determine what timer is currently being executed. * Adds execution count to #DEBUG_TIMERS so shards can get notified (rudimentarily for now) about sequentially executing long chains of timers. Unfortunately, for now, there is no good option when it comes to executing timers. If we execute let's say 500, and defer the rest, that makes all timers effectively 8ms off until it "catches up". Depending on the shard, that may never happen.
This commit is contained in:
parent
919b7e9416
commit
791128f238
3 changed files with 39 additions and 29 deletions
|
|
@ -16,8 +16,8 @@
|
|||
using System;
|
||||
#if DEBUG_TIMERS
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ namespace Server;
|
|||
|
||||
public partial class Timer
|
||||
{
|
||||
#if DEBUG_TIMERS
|
||||
private const int _chainExecutionThreshold = 512;
|
||||
#endif
|
||||
private const int _ringSizePowerOf2 = 12;
|
||||
private const int _ringSize = 1 << _ringSizePowerOf2; // 4096
|
||||
private const int _ringLayers = 3;
|
||||
|
|
@ -33,7 +36,6 @@ public partial class Timer
|
|||
private static Timer[] _executingRings = new Timer[_ringLayers];
|
||||
|
||||
private static long _lastTickTurned = -1;
|
||||
private static bool _timerWheelExecuting;
|
||||
|
||||
public static void Init(long tickCount)
|
||||
{
|
||||
|
|
@ -59,7 +61,6 @@ public partial class Timer
|
|||
|
||||
private static void Turn()
|
||||
{
|
||||
_timerWheelExecuting = true;
|
||||
var turnNextWheel = false;
|
||||
|
||||
// Detach the chain from the timer wheel. This allows adding timers to the same slot during execution.
|
||||
|
|
@ -86,15 +87,19 @@ public partial class Timer
|
|||
|
||||
for (var i = 0; i < _ringLayers; i++)
|
||||
{
|
||||
var timer = _executingRings[i];
|
||||
if (timer == null)
|
||||
#if DEBUG_TIMERS
|
||||
var executionCount = 0;
|
||||
#endif
|
||||
while (_executingRings[i] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#if DEBUG_TIMERS
|
||||
executionCount++;
|
||||
#endif
|
||||
|
||||
do
|
||||
{
|
||||
var next = timer._nextTimer;
|
||||
var timer = _executingRings[i];
|
||||
|
||||
// Set the executing timer to the next in the link list because we will be detaching.
|
||||
_executingRings[i] = timer._nextTimer;
|
||||
|
||||
timer.Detach();
|
||||
|
||||
|
|
@ -116,15 +121,18 @@ public partial class Timer
|
|||
{
|
||||
timer.OnDetach();
|
||||
}
|
||||
|
||||
timer = next;
|
||||
} while (timer != null);
|
||||
|
||||
// Clear out the rings
|
||||
_executingRings[i] = null;
|
||||
}
|
||||
#if DEBUG_TIMERS
|
||||
if (executionCount > _chainExecutionThreshold)
|
||||
{
|
||||
logger.Warning(
|
||||
"Timer threshold of {Threshold} met. Executed {Count} timers sequentially.",
|
||||
_chainExecutionThreshold,
|
||||
executionCount
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
_timerWheelExecuting = false;
|
||||
}
|
||||
|
||||
private static void Execute(Timer timer)
|
||||
|
|
@ -232,7 +240,7 @@ public partial class Timer
|
|||
|
||||
total++;
|
||||
|
||||
t = t?._nextTimer;
|
||||
t = t._nextTimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,20 +153,22 @@ public partial class Timer
|
|||
|
||||
Running = false;
|
||||
|
||||
// Do not detach if we are in the middle of executing the timer wheel for this ring/slot
|
||||
if (!_timerWheelExecuting || _ringIndexes[_ring] != _slot)
|
||||
// We are the head on the timer ring
|
||||
if (_rings[_ring][_slot] == this)
|
||||
{
|
||||
// We are at the head
|
||||
if (_rings[_ring][_slot] == this)
|
||||
{
|
||||
_rings[_ring][_slot] = _nextTimer;
|
||||
}
|
||||
|
||||
Detach();
|
||||
OnDetach();
|
||||
_rings[_ring][_slot] = _nextTimer;
|
||||
}
|
||||
|
||||
// We are the head on the executing ring
|
||||
if (_executingRings[_ring] == this)
|
||||
{
|
||||
_executingRings[_ring] = _nextTimer;
|
||||
}
|
||||
|
||||
Detach();
|
||||
|
||||
Version++;
|
||||
OnDetach();
|
||||
|
||||
var prof = GetProfile();
|
||||
if (prof != null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue