227 lines
8.8 KiB
Java
227 lines
8.8 KiB
Java
/*
|
|
* Copyright 2013 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.util;
|
|
|
|
import org.junit.Test;
|
|
|
|
import java.util.concurrent.BlockingQueue;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
import java.util.concurrent.RejectedExecutionException;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertFalse;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
public class HashedWheelTimerTest {
|
|
|
|
@Test
|
|
public void testScheduleTimeoutShouldNotRunBeforeDelay() throws InterruptedException {
|
|
final Timer timer = new HashedWheelTimer();
|
|
final CountDownLatch barrier = new CountDownLatch(1);
|
|
final Timeout timeout = timer.newTimeout(timeout1 -> {
|
|
fail("This should not have run");
|
|
barrier.countDown();
|
|
}, 10, TimeUnit.SECONDS);
|
|
assertFalse(barrier.await(3, TimeUnit.SECONDS));
|
|
assertFalse("timer should not expire", timeout.isExpired());
|
|
timer.stop();
|
|
}
|
|
|
|
@Test
|
|
public void testScheduleTimeoutShouldRunAfterDelay() throws InterruptedException {
|
|
final Timer timer = new HashedWheelTimer();
|
|
final CountDownLatch barrier = new CountDownLatch(1);
|
|
final Timeout timeout = timer.newTimeout(timeout1 -> barrier.countDown(), 2, TimeUnit.SECONDS);
|
|
assertTrue(barrier.await(3, TimeUnit.SECONDS));
|
|
assertTrue("timer should expire", timeout.isExpired());
|
|
timer.stop();
|
|
}
|
|
|
|
@Test(timeout = 3000)
|
|
public void testStopTimer() throws InterruptedException {
|
|
final CountDownLatch latch = new CountDownLatch(3);
|
|
final Timer timerProcessed = new HashedWheelTimer();
|
|
for (int i = 0; i < 3; i ++) {
|
|
timerProcessed.newTimeout(timeout -> latch.countDown(), 1, TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
latch.await();
|
|
assertEquals("Number of unprocessed timeouts should be 0", 0, timerProcessed.stop().size());
|
|
|
|
final Timer timerUnprocessed = new HashedWheelTimer();
|
|
for (int i = 0; i < 5; i ++) {
|
|
timerUnprocessed.newTimeout(timeout -> {
|
|
}, 5, TimeUnit.SECONDS);
|
|
}
|
|
Thread.sleep(1000L); // sleep for a second
|
|
assertFalse("Number of unprocessed timeouts should be greater than 0", timerUnprocessed.stop().isEmpty());
|
|
}
|
|
|
|
@Test(timeout = 3000)
|
|
public void testTimerShouldThrowExceptionAfterShutdownForNewTimeouts() throws InterruptedException {
|
|
final CountDownLatch latch = new CountDownLatch(3);
|
|
final Timer timer = new HashedWheelTimer();
|
|
for (int i = 0; i < 3; i ++) {
|
|
timer.newTimeout(timeout -> latch.countDown(), 1, TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
latch.await();
|
|
timer.stop();
|
|
|
|
try {
|
|
timer.newTimeout(createNoOpTimerTask(), 1, TimeUnit.MILLISECONDS);
|
|
fail("Expected exception didn't occur.");
|
|
} catch (IllegalStateException ignored) {
|
|
// expected
|
|
}
|
|
}
|
|
|
|
@Test(timeout = 5000)
|
|
public void testTimerOverflowWheelLength() throws InterruptedException {
|
|
final HashedWheelTimer timer = new HashedWheelTimer(
|
|
Executors.defaultThreadFactory(), 100, TimeUnit.MILLISECONDS, 32);
|
|
final CountDownLatch latch = new CountDownLatch(3);
|
|
|
|
timer.newTimeout(new TimerTask() {
|
|
@Override
|
|
public void run(final Timeout timeout) throws Exception {
|
|
timer.newTimeout(this, 100, TimeUnit.MILLISECONDS);
|
|
latch.countDown();
|
|
}
|
|
}, 100, TimeUnit.MILLISECONDS);
|
|
|
|
latch.await();
|
|
assertFalse(timer.stop().isEmpty());
|
|
}
|
|
|
|
@Test
|
|
public void testExecutionOnTime() throws InterruptedException {
|
|
int tickDuration = 200;
|
|
int timeout = 125;
|
|
int maxTimeout = 2 * (tickDuration + timeout);
|
|
final HashedWheelTimer timer = new HashedWheelTimer(tickDuration, TimeUnit.MILLISECONDS);
|
|
final BlockingQueue<Long> queue = new LinkedBlockingQueue<>();
|
|
|
|
int scheduledTasks = 100000;
|
|
for (int i = 0; i < scheduledTasks; i++) {
|
|
final long start = System.nanoTime();
|
|
timer.newTimeout(timeout1 -> queue.add(
|
|
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)), timeout, TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
for (int i = 0; i < scheduledTasks; i++) {
|
|
long delay = queue.take();
|
|
assertTrue("Timeout + " + scheduledTasks + " delay " + delay + " must be " + timeout + " < " + maxTimeout,
|
|
delay >= timeout && delay < maxTimeout);
|
|
}
|
|
|
|
timer.stop();
|
|
}
|
|
|
|
@Test
|
|
public void testRejectedExecutionExceptionWhenTooManyTimeoutsAreAddedBackToBack() {
|
|
HashedWheelTimer timer = new HashedWheelTimer(Executors.defaultThreadFactory(), 100,
|
|
TimeUnit.MILLISECONDS, 32, true, 2);
|
|
timer.newTimeout(createNoOpTimerTask(), 5, TimeUnit.SECONDS);
|
|
timer.newTimeout(createNoOpTimerTask(), 5, TimeUnit.SECONDS);
|
|
try {
|
|
timer.newTimeout(createNoOpTimerTask(), 1, TimeUnit.MILLISECONDS);
|
|
fail("Timer allowed adding 3 timeouts when maxPendingTimeouts was 2");
|
|
} catch (RejectedExecutionException e) {
|
|
// Expected
|
|
} finally {
|
|
timer.stop();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testNewTimeoutShouldStopThrowingRejectedExecutionExceptionWhenExistingTimeoutIsCancelled()
|
|
throws InterruptedException {
|
|
final int tickDurationMs = 100;
|
|
final HashedWheelTimer timer = new HashedWheelTimer(Executors.defaultThreadFactory(), tickDurationMs,
|
|
TimeUnit.MILLISECONDS, 32, true, 2);
|
|
timer.newTimeout(createNoOpTimerTask(), 5, TimeUnit.SECONDS);
|
|
Timeout timeoutToCancel = timer.newTimeout(createNoOpTimerTask(), 5, TimeUnit.SECONDS);
|
|
assertTrue(timeoutToCancel.cancel());
|
|
|
|
Thread.sleep(tickDurationMs * 5);
|
|
|
|
final CountDownLatch secondLatch = new CountDownLatch(1);
|
|
timer.newTimeout(createCountDownLatchTimerTask(secondLatch), 90, TimeUnit.MILLISECONDS);
|
|
|
|
secondLatch.await();
|
|
timer.stop();
|
|
}
|
|
|
|
@Test(timeout = 3000)
|
|
public void testNewTimeoutShouldStopThrowingRejectedExecutionExceptionWhenExistingTimeoutIsExecuted()
|
|
throws InterruptedException {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
final HashedWheelTimer timer = new HashedWheelTimer(Executors.defaultThreadFactory(), 25,
|
|
TimeUnit.MILLISECONDS, 4, true, 2);
|
|
timer.newTimeout(createNoOpTimerTask(), 5, TimeUnit.SECONDS);
|
|
timer.newTimeout(createCountDownLatchTimerTask(latch), 90, TimeUnit.MILLISECONDS);
|
|
|
|
latch.await();
|
|
|
|
final CountDownLatch secondLatch = new CountDownLatch(1);
|
|
timer.newTimeout(createCountDownLatchTimerTask(secondLatch), 90, TimeUnit.MILLISECONDS);
|
|
|
|
secondLatch.await();
|
|
timer.stop();
|
|
}
|
|
|
|
@Test()
|
|
public void reportPendingTimeouts() throws InterruptedException {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
final HashedWheelTimer timer = new HashedWheelTimer();
|
|
final Timeout t1 = timer.newTimeout(createNoOpTimerTask(), 100, TimeUnit.MINUTES);
|
|
final Timeout t2 = timer.newTimeout(createNoOpTimerTask(), 100, TimeUnit.MINUTES);
|
|
timer.newTimeout(createCountDownLatchTimerTask(latch), 90, TimeUnit.MILLISECONDS);
|
|
|
|
assertEquals(3, timer.pendingTimeouts());
|
|
t1.cancel();
|
|
t2.cancel();
|
|
latch.await();
|
|
|
|
assertEquals(0, timer.pendingTimeouts());
|
|
timer.stop();
|
|
}
|
|
|
|
@Test
|
|
public void testOverflow() throws InterruptedException {
|
|
final HashedWheelTimer timer = new HashedWheelTimer();
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
Timeout timeout = timer.newTimeout(timeout1 -> latch.countDown(), Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
|
assertFalse(latch.await(1, TimeUnit.SECONDS));
|
|
timeout.cancel();
|
|
timer.stop();
|
|
}
|
|
|
|
private static TimerTask createNoOpTimerTask() {
|
|
return timeout -> {
|
|
};
|
|
}
|
|
|
|
private static TimerTask createCountDownLatchTimerTask(final CountDownLatch latch) {
|
|
return timeout -> latch.countDown();
|
|
}
|
|
}
|