fix: Fixes timer Delay/Next not handling MinValue inputs (#1985)

### Summary

Fixes a few minor issues with timers:
- Timer.Delay and Timer.Interval was not reflecting the actual tick time (aligned to the next 8ms)
- Negative delay values were causing a crash when DateTime.Now - delay was below DateTime.MinValue
- Timer.Next now reflects the correct wall clock tick time based on the adjusted Delay.
- Timer.Next is not assigned when the timer is started. This was important for timers that were created, but started later.
This commit is contained in:
Kamron Batman 2024-10-28 17:48:16 -07:00 • committed by GitHub
parent ac06a0d52d
commit cc6d029add
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 124 additions and 100 deletions

View file

@ -1,108 +1,108 @@
using System;
using Xunit;
namespace Server.Tests
namespace Server.Tests;
[Collection("Sequential Tests")]
public class TimerTests : IClassFixture<ServerFixture>
{
[Collection("Sequential Tests")]
public class TimerTests : IClassFixture<ServerFixture>
[Theory]
[InlineData(0L, 8L)]
[InlineData(80L, 80L)]
[InlineData(65L, 72L)]
[InlineData(32767L, 32768L)]
[InlineData(32768L, 32768L)]
[InlineData(32833L, 32840L)]
[InlineData(134217729L, 134217736L)]
public void TestVariousTimes(long ticks, long expectedTicks)
{
[Theory]
[InlineData(0L, 8L)]
[InlineData(80L, 80L)]
[InlineData(65L, 72L)]
[InlineData(32767L, 32768L)]
[InlineData(32768L, 32768L)]
[InlineData(32833L, 32840L)]
[InlineData(134217729L, 134217736L)]
public void TestVariousTimes(long ticks, long expectedTicks)
var timerTicks = new TimerTicks();
Timer.Init(timerTicks.Ticks);
Timer.StartTimer(TimeSpan.FromMilliseconds(ticks), action);
var tickCount = expectedTicks / 8;
for (int i = 1; i <= tickCount; i++)
{
var timerTicks = new TimerTicks();
void action()
{
Assert.Equal(expectedTicks, timerTicks.Ticks);
timerTicks.ExecutedCount++;
}
timerTicks.Ticks = i * 8;
Timer.Init(timerTicks.Ticks);
Timer.StartTimer(TimeSpan.FromMilliseconds(ticks), action);
var tickCount = expectedTicks / 8;
for (int i = 1; i <= tickCount; i++)
{
timerTicks.Ticks = i * 8;
Timer.Slice(timerTicks.Ticks);
}
Assert.Equal(1, timerTicks.ExecutedCount);
Timer.Slice(timerTicks.Ticks);
}
[Theory]
[InlineData(1000L, 1000L, 30000L, 30000L, 2)]
public void TestIntervals(long delay, long expectedDelayTicks, long interval, long expectedIntervalTicks, int count)
Assert.Equal(1, timerTicks.ExecutedCount);
return;
void action()
{
var timerTicks = new TimerTicks();
Assert.Equal(expectedTicks, timerTicks.Ticks);
timerTicks.ExecutedCount++;
}
}
Timer.Init(timerTicks.Ticks);
[Theory]
[InlineData(1000L, 1000L, 30000L, 30000L, 2)]
public void TestIntervals(long delay, long expectedDelayTicks, long interval, long expectedIntervalTicks, int count)
{
var timerTicks = new TimerTicks();
Timer.StartTimer(TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), count, action);
Timer.Init(timerTicks.Ticks);
var tickCount = (expectedDelayTicks + (expectedIntervalTicks * count - 1)) / 8;
Timer.StartTimer(TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), count, action);
for (int i = 1; i <= tickCount; i++)
{
timerTicks.Ticks = i * 8;
var tickCount = (expectedDelayTicks + (expectedIntervalTicks * count - 1)) / 8;
Timer.Slice(timerTicks.Ticks);
}
for (int i = 1; i <= tickCount; i++)
{
timerTicks.Ticks = i * 8;
Assert.Equal(count, timerTicks.ExecutedCount);
return;
void action()
{
timerTicks.ExpectedTicks += timerTicks.ExecutedCount++ == 0 ? expectedDelayTicks : expectedIntervalTicks;
Assert.Equal(timerTicks.ExpectedTicks, timerTicks.Ticks);
}
Timer.Slice(timerTicks.Ticks);
}
[Fact]
public void TestTimerStartedOnTick()
Assert.Equal(count, timerTicks.ExecutedCount);
return;
void action()
{
var timerTicks = new TimerTicks();
Timer.Init(timerTicks.Ticks);
var timer = new SelfRunningTimer(timerTicks);
timer.Start();
Timer.Slice(128);
Assert.Equal(1, timerTicks.ExecutedCount);
Timer.Slice(256);
Assert.Equal(2, timerTicks.ExecutedCount);
timerTicks.ExpectedTicks += timerTicks.ExecutedCount++ == 0 ? expectedDelayTicks : expectedIntervalTicks;
Assert.Equal(timerTicks.ExpectedTicks, timerTicks.Ticks);
}
}
private class TimerTicks
[Fact]
public void TestTimerStartedOnTick()
{
var timerTicks = new TimerTicks();
Timer.Init(timerTicks.Ticks);
var timer = new SelfRunningTimer(timerTicks);
timer.Start();
Timer.Slice(128);
Assert.Equal(1, timerTicks.ExecutedCount);
Timer.Slice(256);
Assert.Equal(2, timerTicks.ExecutedCount);
}
private class TimerTicks
{
public long ExpectedTicks;
public long Ticks;
public int ExecutedCount;
}
private class SelfRunningTimer : Timer
{
private readonly TimerTicks _timerTicks;
public SelfRunningTimer(TimerTicks ticks) : base(TimeSpan.FromMilliseconds(100)) => _timerTicks = ticks;
protected override void OnTick()
{
public long ExpectedTicks;
public long Ticks;
public int ExecutedCount;
}
private class SelfRunningTimer : Timer
{
private readonly TimerTicks _timerTicks;
public SelfRunningTimer(TimerTicks ticks) : base(TimeSpan.FromMilliseconds(100)) => _timerTicks = ticks;
protected override void OnTick()
if (_timerTicks.ExecutedCount++ == 0)
{
if (_timerTicks.ExecutedCount++ == 0)
{
Delay = TimeSpan.FromMilliseconds(100);
Start();
}
Delay = TimeSpan.FromMilliseconds(100);
Start();
}
}
}