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]; var timer = _rings[i][ringIndex];
if (timer != null) if (timer != null)
{ {
events = true; events = true;
if (i > 0 && timer._remaining > 0) do
{ {
Promote(timer); var next = _rings[i][ringIndex] = timer._nextTimer;
}
else
{
Execute(timer);
}
// Clear the slot timer.Detach();
_rings[i][ringIndex] = null;
if (i > 0 && timer._remaining > 0)
{
// Promote
AddTimer(timer, timer._remaining);
}
else
{
Execute(timer);
}
timer = next;
} while (timer != null);
} }
if (!turnNextWheel) if (!turnNextWheel)
@ -103,50 +109,29 @@ namespace Server
private static void Execute(Timer timer) 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; if (finished)
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) timer.Stop();
{
timer.Stop();
}
else
{
timer.Delay = timer.Interval;
timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
}
} }
else
timer = next; {
} while (timer != null); timer.Delay = timer.Interval;
} timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
private static void Promote(Timer timer) }
{ }
do
{
var next = timer._nextTimer;
RemoveTimer(timer);
AddTimer(timer, timer._remaining);
timer = next;
} while (timer != null);
} }
private static void AddTimer(Timer timer, long delay) private static void AddTimer(Timer timer, long delay)
@ -182,6 +167,7 @@ namespace Server
timer._slot = (int)slot; timer._slot = (int)slot;
_rings[i][slot] = timer; _rings[i][slot] = timer;
break; 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) public static void DumpInfo(TextWriter tw)
{ {
tw.WriteLine("Date: {0}\n", Core.Now.ToLocalTime()); tw.WriteLine("Date: {0}\n", Core.Now.ToLocalTime());

View file

@ -81,6 +81,7 @@ namespace Server
return this; return this;
} }
Index = 0;
Running = true; Running = true;
AddTimer(this, (long)Delay.TotalMilliseconds); AddTimer(this, (long)Delay.TotalMilliseconds);
@ -101,8 +102,15 @@ namespace Server
return; return;
} }
RemoveTimer(this); // We are at the head
if (_rings[_ring][_slot] == this)
{
_rings[_ring][_slot] = _nextTimer;
}
Detach();
Running = false; Running = false;
Version++;
var prof = GetProfile(); var prof = GetProfile();
if (prof != null) if (prof != null)

View file

@ -162,6 +162,8 @@ namespace Server
using FileStream bin = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); using FileStream bin = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read);
BufferReader br = null; BufferReader br = null;
var deleteAllFailures = false;
foreach (var entry in entities) foreach (var entry in entities)
{ {
T t = entry.Entity; T t = entry.Entity;
@ -210,11 +212,21 @@ namespace Server
Console.WriteLine($"***** Bad deserialize of {t.GetType()} *****"); Console.WriteLine($"***** Bad deserialize of {t.GetType()} *****");
Console.WriteLine(error); Console.WriteLine(error);
Console.WriteLine("Delete the object and continue? (y/n)"); ConsoleKey pressedKey;
if (Console.ReadKey(true).Key != ConsoleKey.Y) if (!deleteAllFailures)
{ {
throw new Exception("Deserialization failed."); Console.WriteLine("Delete the object and continue? (y/n/a)");
pressedKey = Console.ReadKey(true).Key;
if (pressedKey == ConsoleKey.A)
{
deleteAllFailures = true;
}
else if (pressedKey != ConsoleKey.Y)
{
throw new Exception("Deserialization failed.");
}
} }
t.Delete(); t.Delete();

View file

@ -110,7 +110,11 @@ namespace Server.Engines.Spawners
{ {
m_Count = value; m_Count = value;
if (m_Timer != null && (!IsFull && !m_Timer.Running || IsFull && m_Timer.Running)) if (IsFull)
{
m_Timer?.Stop();
}
else if (m_Timer?.Running != true)
{ {
DoTimer(); DoTimer();
} }
@ -372,11 +376,8 @@ namespace Server.Engines.Spawners
public void Stop() public void Stop()
{ {
if (m_Running) m_Timer?.Stop();
{ m_Running = false;
m_Timer?.Stop();
m_Running = false;
}
} }
public void Defrag() public void Defrag()