fix(timers): Fixes multiple issue with timer wheel (#705)

* Fixed an issue where a timer truncated the link list.
* Fixed an issue where a timer stopped and started itself within an OnTick causing it to remove itself from the link list, but still think it's running.
This commit is contained in:
Kamron Batman 2021-08-20 18:04:34 -07:00 committed by GitHub
parent c2f853d8a8
commit 1d916fe2df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 71 deletions

View file

@ -74,22 +74,28 @@ namespace Server
}
var timer = _rings[i][ringIndex];
if (timer != null)
{
events = true;
if (i > 0 && timer._remaining > 0)
do
{
Promote(timer);
}
else
{
Execute(timer);
}
var next = _rings[i][ringIndex] = timer._nextTimer;
// Clear the slot
_rings[i][ringIndex] = null;
timer.Detach();
if (i > 0 && timer._remaining > 0)
{
// Promote
AddTimer(timer, timer._remaining);
}
else
{
Execute(timer);
}
timer = next;
} while (timer != null);
}
if (!turnNextWheel)
@ -103,50 +109,29 @@ namespace Server
private static void Execute(Timer timer)
{
do
var finished = timer.Count != 0 && ++timer.Index >= timer.Count;
var version = timer.Version;
var prof = timer.GetProfile();
prof?.Start();
timer.OnTick();
prof?.Finish();
// If the timer has not been stopped, and it has not been altered (restarted, returned etc)
if (timer.Running && timer.Version == version)
{
var next = timer._nextTimer;
var prof = timer.GetProfile();
var finished = timer.Count != 0 && ++timer.Index >= timer.Count;
// We remove it before `OnTick()` so time references can be nulled and returned to cache safely from within OnTick.
// This can be done in OnTick by checking if Index < Count - 1 (still more iterations left)
RemoveTimer(timer);
var version = timer.Version;
prof?.Start();
timer.OnTick();
prof?.Finish();
// If the timer has not been stopped, and it has not been altered (shared timers)
if (timer.Running && timer.Version == version)
if (finished)
{
if (finished)
{
timer.Stop();
}
else
{
timer.Delay = timer.Interval;
timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
}
timer.Stop();
}
timer = next;
} while (timer != null);
}
private static void Promote(Timer timer)
{
do
{
var next = timer._nextTimer;
RemoveTimer(timer);
AddTimer(timer, timer._remaining);
timer = next;
} while (timer != null);
else
{
timer.Delay = timer.Interval;
timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
}
}
}
private static void AddTimer(Timer timer, long delay)
@ -182,6 +167,7 @@ namespace Server
timer._slot = (int)slot;
_rings[i][slot] = timer;
break;
}
@ -191,16 +177,6 @@ namespace Server
}
}
private static void RemoveTimer(Timer timer)
{
if (timer._prevTimer == null)
{
_rings[timer._ring][timer._slot] = timer._nextTimer;
}
timer.Detach();
}
public static void DumpInfo(TextWriter tw)
{
tw.WriteLine("Date: {0}\n", Core.Now.ToLocalTime());