mirror of
https://github.com/Azure/DotNetty.git
synced 2026-10-18 22:30:36 +08:00
977fd6ac09
Signed-off-by: nivalxer <nivalxer@gmail.com>
211 lines
7.9 KiB
C#
211 lines
7.9 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
namespace DotNetty.Common.Tests.Utilities
|
|
{
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Threading;
|
|
using DotNetty.Common.Concurrency;
|
|
using DotNetty.Common.Utilities;
|
|
using DotNetty.Tests.Common;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
using ITimer = DotNetty.Common.Utilities.ITimer;
|
|
|
|
public class HashedWheelTimerTest : TestBase
|
|
{
|
|
public HashedWheelTimerTest(ITestOutputHelper output)
|
|
: base(output)
|
|
{
|
|
}
|
|
|
|
[Fact]
|
|
public void TestScheduleTimeoutShouldNotRunBeforeDelay()
|
|
{
|
|
ITimer timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(100), 512, -1);
|
|
var barrier = new CountdownEvent(1);
|
|
ITimeout timeout = timer.NewTimeout(
|
|
new ActionTimerTask(
|
|
t =>
|
|
{
|
|
Assert.True(false, "This should not have run");
|
|
barrier.Signal();
|
|
}),
|
|
TimeSpan.FromSeconds(10));
|
|
Assert.False(barrier.Wait(TimeSpan.FromSeconds(3)));
|
|
Assert.False(timeout.Expired, "timer should not expire");
|
|
timer.StopAsync().Wait();
|
|
}
|
|
|
|
[Fact]
|
|
public void TestScheduleTimeoutShouldRunAfterDelay()
|
|
{
|
|
ITimer timer = new HashedWheelTimer();
|
|
var barrier = new CountdownEvent(1);
|
|
ITimeout timeout = timer.NewTimeout(
|
|
new ActionTimerTask(
|
|
t => { barrier.Signal(); }),
|
|
TimeSpan.FromSeconds(2));
|
|
Assert.True(barrier.Wait(TimeSpan.FromSeconds(3)));
|
|
Assert.True(timeout.Expired, "timer should expire");
|
|
timer.StopAsync().Wait();
|
|
}
|
|
|
|
[Fact] // (timeout = 3000)
|
|
public void TestStopTimer()
|
|
{
|
|
var latch = new CountdownEvent(3);
|
|
ITimer timerProcessed = new HashedWheelTimer();
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
timerProcessed.NewTimeout(
|
|
new ActionTimerTask(
|
|
t => { latch.Signal(); }),
|
|
TimeSpan.FromMilliseconds(1));
|
|
}
|
|
|
|
latch.Wait();
|
|
Assert.Equal(0, timerProcessed.StopAsync().Result.Count); // "Number of unprocessed timeouts should be 0"
|
|
|
|
ITimer timerUnprocessed = new HashedWheelTimer();
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
timerUnprocessed.NewTimeout(
|
|
new ActionTimerTask(
|
|
t => { }),
|
|
TimeSpan.FromSeconds(5));
|
|
}
|
|
Thread.Sleep(1000); // sleep for a second
|
|
Assert.NotEqual(0, timerUnprocessed.StopAsync().Result.Count); // Number of unprocessed timeouts should be greater than 0
|
|
}
|
|
|
|
[Fact] // (timeout = 3000)
|
|
public void TestTimerShouldThrowExceptionAfterShutdownForNewTimeouts()
|
|
{
|
|
var latch = new CountdownEvent(3);
|
|
ITimer timer = new HashedWheelTimer();
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
timer.NewTimeout(
|
|
new ActionTimerTask(
|
|
t => { latch.Signal(); }),
|
|
TimeSpan.FromMilliseconds(1));
|
|
}
|
|
|
|
latch.Wait(3000);
|
|
timer.StopAsync().Wait();
|
|
|
|
Assert.Throws<RejectedExecutionException>(() => timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromMilliseconds(1)));
|
|
}
|
|
|
|
[Fact] // (timeout = 5000)
|
|
public void TestTimerOverflowWheelLength()
|
|
{
|
|
var timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(100), 32, -1);
|
|
var latch = new CountdownEvent(3);
|
|
|
|
ActionTimerTask task = null;
|
|
task = new ActionTimerTask(
|
|
t =>
|
|
{
|
|
timer.NewTimeout(task, TimeSpan.FromMilliseconds(100));
|
|
latch.Signal();
|
|
});
|
|
timer.NewTimeout(task, TimeSpan.FromMilliseconds(100));
|
|
|
|
Assert.True(latch.Wait(5000));
|
|
Assert.NotEqual(0, timer.StopAsync().Result.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExecutionOnTime()
|
|
{
|
|
int tickDuration = 200;
|
|
int timeout = 125;
|
|
int maxTimeout = 2 * (tickDuration + timeout);
|
|
var timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(tickDuration), 512, -1);
|
|
var queue = new BlockingCollection<PreciseTimeSpan>();
|
|
|
|
int scheduledTasks = 10000;
|
|
for (int i = 0; i < scheduledTasks; i++)
|
|
{
|
|
PreciseTimeSpan start = PreciseTimeSpan.FromStart;
|
|
timer.NewTimeout(
|
|
new ActionTimerTask(
|
|
t => { queue.Add(PreciseTimeSpan.FromStart - start); }),
|
|
TimeSpan.FromMilliseconds(timeout));
|
|
}
|
|
|
|
for (int i = 0; i < scheduledTasks; i++)
|
|
{
|
|
double delay = queue.Take().ToTimeSpan().TotalMilliseconds;
|
|
Assert.True(delay >= timeout && delay < maxTimeout, "Timeout + " + scheduledTasks + " delay " + delay + " must be " + timeout + " < " + maxTimeout);
|
|
}
|
|
|
|
timer.StopAsync().Wait();
|
|
}
|
|
|
|
[Fact]
|
|
public void TestRejectedExecutionExceptionWhenTooManyTimeoutsAreAddedBackToBack()
|
|
{
|
|
var timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(100), 32, 2);
|
|
timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromSeconds(5));
|
|
timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromSeconds(5));
|
|
try
|
|
{
|
|
timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromMilliseconds(1));
|
|
Assert.True(false, "Timer allowed adding 3 timeouts when maxPendingTimeouts was 2");
|
|
}
|
|
catch (RejectedExecutionException)
|
|
{
|
|
// Expected
|
|
}
|
|
finally
|
|
{
|
|
timer.StopAsync().Wait();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TestNewTimeoutShouldStopThrowingRejectedExecutionExceptionWhenExistingTimeoutIsCancelled()
|
|
|
|
{
|
|
int tickDurationMs = 100;
|
|
var timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(tickDurationMs), 32, 2);
|
|
timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromSeconds(5));
|
|
ITimeout timeoutToCancel = timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromSeconds(5));
|
|
Assert.True(timeoutToCancel.Cancel());
|
|
|
|
Thread.Sleep(tickDurationMs * 5);
|
|
|
|
var secondLatch = new CountdownEvent(1);
|
|
timer.NewTimeout(CreateCountdownEventTimerTask(secondLatch), TimeSpan.FromMilliseconds(90));
|
|
|
|
secondLatch.Wait();
|
|
timer.StopAsync().Wait();
|
|
}
|
|
|
|
[Fact] // (timeout = 3000)
|
|
public void TestNewTimeoutShouldStopThrowingRejectedExecutionExceptionWhenExistingTimeoutIsExecuted()
|
|
|
|
{
|
|
var latch = new CountdownEvent(1);
|
|
var timer = new HashedWheelTimer(TimeSpan.FromMilliseconds(25), 4, 2);
|
|
timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromSeconds(5));
|
|
timer.NewTimeout(CreateCountdownEventTimerTask(latch), TimeSpan.FromMilliseconds(90));
|
|
|
|
latch.Wait(3000);
|
|
|
|
var secondLatch = new CountdownEvent(1);
|
|
timer.NewTimeout(CreateCountdownEventTimerTask(secondLatch), TimeSpan.FromMilliseconds(90));
|
|
|
|
secondLatch.Wait(3000);
|
|
timer.StopAsync().Wait();
|
|
}
|
|
|
|
static ActionTimerTask CreateNoOpTimerTask() => new ActionTimerTask(t => { });
|
|
|
|
static ActionTimerTask CreateCountdownEventTimerTask(CountdownEvent latch) => new ActionTimerTask(t => { latch.Signal(); });
|
|
}
|
|
} |