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:
parent
c2f853d8a8
commit
1d916fe2df
4 changed files with 68 additions and 71 deletions
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue